diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint')
24 files changed, 24896 insertions, 24182 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index e7a8c704fda..cddaf69b965 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -16,61 +16,61 @@ # ***** END GPL LICENSE BLOCK ***** set(INC - ../include - ../uvedit - ../../blenkernel - ../../blenlib - ../../blentranslation - ../../bmesh - ../../depsgraph - ../../gpu - ../../imbuf - ../../makesdna - ../../makesrna - ../../render/extern/include - ../../windowmanager - ../../../../intern/guardedalloc - ../../../../intern/glew-mx + ../include + ../uvedit + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../bmesh + ../../depsgraph + ../../gpu + ../../imbuf + ../../makesdna + ../../makesrna + ../../render/extern/include + ../../windowmanager + ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS - ${GLEW_INCLUDE_PATH} + ${GLEW_INCLUDE_PATH} ) set(SRC - paint_cursor.c - paint_curve.c - paint_curve_undo.c - paint_hide.c - paint_image.c - paint_image_2d.c - paint_image_proj.c - paint_image_undo.c - paint_mask.c - paint_ops.c - paint_stroke.c - paint_utils.c - paint_vertex.c - paint_vertex_color_ops.c - paint_vertex_color_utils.c - paint_vertex_proj.c - paint_vertex_weight_ops.c - paint_vertex_weight_utils.c - sculpt.c - sculpt_undo.c - sculpt_uv.c + paint_cursor.c + paint_curve.c + paint_curve_undo.c + paint_hide.c + paint_image.c + paint_image_2d.c + paint_image_proj.c + paint_image_undo.c + paint_mask.c + paint_ops.c + paint_stroke.c + paint_utils.c + paint_vertex.c + paint_vertex_color_ops.c + paint_vertex_color_utils.c + paint_vertex_proj.c + paint_vertex_weight_ops.c + paint_vertex_weight_utils.c + sculpt.c + sculpt_undo.c + sculpt_uv.c - paint_intern.h - sculpt_intern.h + paint_intern.h + sculpt_intern.h ) set(LIB - bf_blenkernel - bf_blenlib + bf_blenkernel + bf_blenlib ) if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) endif() add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 4b714e03d3b..da183d190fa 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -75,1103 +75,1101 @@ */ typedef struct TexSnapshot { - GLuint overlay_texture; - int winx; - int winy; - int old_size; - float old_zoom; - bool old_col; + GLuint overlay_texture; + int winx; + int winy; + int old_size; + float old_zoom; + bool old_col; } TexSnapshot; typedef struct CursorSnapshot { - GLuint overlay_texture; - int size; - int zoom; + GLuint overlay_texture; + int size; + int zoom; } CursorSnapshot; static TexSnapshot primary_snap = {0}; -static TexSnapshot secondary_snap = {0}; -static CursorSnapshot cursor_snap = {0}; +static TexSnapshot secondary_snap = {0}; +static CursorSnapshot cursor_snap = {0}; /* delete overlay cursor textures to preserve memory and invalidate all overlay flags */ void paint_cursor_delete_textures(void) { - if (primary_snap.overlay_texture) - glDeleteTextures(1, &primary_snap.overlay_texture); - if (secondary_snap.overlay_texture) - glDeleteTextures(1, &secondary_snap.overlay_texture); - if (cursor_snap.overlay_texture) - glDeleteTextures(1, &cursor_snap.overlay_texture); - - memset(&primary_snap, 0, sizeof(TexSnapshot)); - memset(&secondary_snap, 0, sizeof(TexSnapshot)); - memset(&cursor_snap, 0, sizeof(CursorSnapshot)); - - BKE_paint_invalidate_overlay_all(); + if (primary_snap.overlay_texture) + glDeleteTextures(1, &primary_snap.overlay_texture); + if (secondary_snap.overlay_texture) + glDeleteTextures(1, &secondary_snap.overlay_texture); + if (cursor_snap.overlay_texture) + glDeleteTextures(1, &cursor_snap.overlay_texture); + + memset(&primary_snap, 0, sizeof(TexSnapshot)); + memset(&secondary_snap, 0, sizeof(TexSnapshot)); + memset(&cursor_snap, 0, sizeof(CursorSnapshot)); + + BKE_paint_invalidate_overlay_all(); } static int same_tex_snap(TexSnapshot *snap, MTex *mtex, ViewContext *vc, bool col, float zoom) { - return (/* make brush smaller shouldn't cause a resample */ - //(mtex->brush_map_mode != MTEX_MAP_MODE_VIEW || - //(BKE_brush_size_get(vc->scene, brush) <= snap->BKE_brush_size_get)) && - - (mtex->brush_map_mode != MTEX_MAP_MODE_TILED || - (vc->ar->winx == snap->winx && - vc->ar->winy == snap->winy)) && - (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL || - snap->old_zoom == zoom) && - snap->old_col == col - ); + return (/* make brush smaller shouldn't cause a resample */ + //(mtex->brush_map_mode != MTEX_MAP_MODE_VIEW || + //(BKE_brush_size_get(vc->scene, brush) <= snap->BKE_brush_size_get)) && + + (mtex->brush_map_mode != MTEX_MAP_MODE_TILED || + (vc->ar->winx == snap->winx && vc->ar->winy == snap->winy)) && + (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL || snap->old_zoom == zoom) && + snap->old_col == col); } static void make_tex_snap(TexSnapshot *snap, ViewContext *vc, float zoom) { - snap->old_zoom = zoom; - snap->winx = vc->ar->winx; - snap->winy = vc->ar->winy; + snap->old_zoom = zoom; + snap->winx = vc->ar->winx; + snap->winy = vc->ar->winy; } typedef struct LoadTexData { - Brush *br; - ViewContext *vc; + Brush *br; + ViewContext *vc; - MTex *mtex; - GLubyte *buffer; - bool col; + MTex *mtex; + GLubyte *buffer; + bool col; - struct ImagePool *pool; - int size; - float rotation; - float radius; + struct ImagePool *pool; + int size; + float rotation; + float radius; } LoadTexData; -static void load_tex_task_cb_ex( - void *__restrict userdata, - const int j, - const ParallelRangeTLS *__restrict tls) +static void load_tex_task_cb_ex(void *__restrict userdata, + const int j, + const ParallelRangeTLS *__restrict tls) { - LoadTexData *data = userdata; - Brush *br = data->br; - ViewContext *vc = data->vc; - - MTex *mtex = data->mtex; - GLubyte *buffer = data->buffer; - const bool col = data->col; - - struct ImagePool *pool = data->pool; - const int size = data->size; - const float rotation = data->rotation; - const float radius = data->radius; - - bool convert_to_linear = false; - struct ColorSpace *colorspace = NULL; - - if (mtex->tex && mtex->tex->type == TEX_IMAGE && mtex->tex->ima) { - ImBuf *tex_ibuf = BKE_image_pool_acquire_ibuf(mtex->tex->ima, &mtex->tex->iuser, pool); - /* For consistency, sampling always returns color in linear space */ - if (tex_ibuf && tex_ibuf->rect_float == NULL) { - convert_to_linear = true; - colorspace = tex_ibuf->rect_colorspace; - } - BKE_image_pool_release_ibuf(mtex->tex->ima, tex_ibuf, pool); - } - - for (int i = 0; i < size; i++) { - // largely duplicated from tex_strength - - int index = j * size + i; - - float x = (float)i / size; - float y = (float)j / size; - float len; - - if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { - x *= vc->ar->winx / radius; - y *= vc->ar->winy / radius; - } - else { - x = (x - 0.5f) * 2.0f; - y = (y - 0.5f) * 2.0f; - } - - len = sqrtf(x * x + y * y); - - if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL) || len <= 1.0f) { - /* it is probably worth optimizing for those cases where the texture is not rotated by skipping the calls to - * atan2, sqrtf, sin, and cos. */ - if (mtex->tex && (rotation > 0.001f || rotation < -0.001f)) { - const float angle = atan2f(y, x) + rotation; - - x = len * cosf(angle); - y = len * sinf(angle); - } - - if (col) { - float rgba[4]; - - paint_get_tex_pixel_col(mtex, x, y, rgba, pool, tls->thread_id, convert_to_linear, colorspace); - - buffer[index * 4] = rgba[0] * 255; - buffer[index * 4 + 1] = rgba[1] * 255; - buffer[index * 4 + 2] = rgba[2] * 255; - buffer[index * 4 + 3] = rgba[3] * 255; - } - else { - float avg = paint_get_tex_pixel(mtex, x, y, pool, tls->thread_id); - - avg += br->texture_sample_bias; - - /* clamp to avoid precision overflow */ - CLAMP(avg, 0.0f, 1.0f); - buffer[index] = 255 - (GLubyte)(255 * avg); - } - } - else { - if (col) { - buffer[index * 4] = 0; - buffer[index * 4 + 1] = 0; - buffer[index * 4 + 2] = 0; - buffer[index * 4 + 3] = 0; - } - else { - buffer[index] = 0; - } - } - } + LoadTexData *data = userdata; + Brush *br = data->br; + ViewContext *vc = data->vc; + + MTex *mtex = data->mtex; + GLubyte *buffer = data->buffer; + const bool col = data->col; + + struct ImagePool *pool = data->pool; + const int size = data->size; + const float rotation = data->rotation; + const float radius = data->radius; + + bool convert_to_linear = false; + struct ColorSpace *colorspace = NULL; + + if (mtex->tex && mtex->tex->type == TEX_IMAGE && mtex->tex->ima) { + ImBuf *tex_ibuf = BKE_image_pool_acquire_ibuf(mtex->tex->ima, &mtex->tex->iuser, pool); + /* For consistency, sampling always returns color in linear space */ + if (tex_ibuf && tex_ibuf->rect_float == NULL) { + convert_to_linear = true; + colorspace = tex_ibuf->rect_colorspace; + } + BKE_image_pool_release_ibuf(mtex->tex->ima, tex_ibuf, pool); + } + + for (int i = 0; i < size; i++) { + // largely duplicated from tex_strength + + int index = j * size + i; + + float x = (float)i / size; + float y = (float)j / size; + float len; + + if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { + x *= vc->ar->winx / radius; + y *= vc->ar->winy / radius; + } + else { + x = (x - 0.5f) * 2.0f; + y = (y - 0.5f) * 2.0f; + } + + len = sqrtf(x * x + y * y); + + if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL) || len <= 1.0f) { + /* it is probably worth optimizing for those cases where the texture is not rotated by skipping the calls to + * atan2, sqrtf, sin, and cos. */ + if (mtex->tex && (rotation > 0.001f || rotation < -0.001f)) { + const float angle = atan2f(y, x) + rotation; + + x = len * cosf(angle); + y = len * sinf(angle); + } + + if (col) { + float rgba[4]; + + paint_get_tex_pixel_col( + mtex, x, y, rgba, pool, tls->thread_id, convert_to_linear, colorspace); + + buffer[index * 4] = rgba[0] * 255; + buffer[index * 4 + 1] = rgba[1] * 255; + buffer[index * 4 + 2] = rgba[2] * 255; + buffer[index * 4 + 3] = rgba[3] * 255; + } + else { + float avg = paint_get_tex_pixel(mtex, x, y, pool, tls->thread_id); + + avg += br->texture_sample_bias; + + /* clamp to avoid precision overflow */ + CLAMP(avg, 0.0f, 1.0f); + buffer[index] = 255 - (GLubyte)(255 * avg); + } + } + else { + if (col) { + buffer[index * 4] = 0; + buffer[index * 4 + 1] = 0; + buffer[index * 4 + 2] = 0; + buffer[index * 4 + 3] = 0; + } + else { + buffer[index] = 0; + } + } + } } static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool primary) { - bool init; - TexSnapshot *target; - - MTex *mtex = (primary) ? &br->mtex : &br->mask_mtex; - eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); - GLubyte *buffer = NULL; - - int size; - bool refresh; - eOverlayControlFlags invalid = ( - (primary) ? - (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY) : - (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY)); - target = (primary) ? &primary_snap : &secondary_snap; - - refresh = - !target->overlay_texture || - (invalid != 0) || - !same_tex_snap(target, mtex, vc, col, zoom); - - init = (target->overlay_texture != 0); - - if (refresh) { - struct ImagePool *pool = NULL; - /* stencil is rotated later */ - const float rotation = (mtex->brush_map_mode != MTEX_MAP_MODE_STENCIL) ? -mtex->rot : 0.0f; - const float radius = BKE_brush_size_get(vc->scene, br) * zoom; - - make_tex_snap(target, vc, zoom); - - if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { - int s = BKE_brush_size_get(vc->scene, br); - int r = 1; - - for (s >>= 1; s > 0; s >>= 1) - r++; - - size = (1 << r); - - if (size < 256) - size = 256; - - if (size < target->old_size) - size = target->old_size; - } - else { - size = 512; - } - - if (target->old_size != size) { - if (target->overlay_texture) { - glDeleteTextures(1, &target->overlay_texture); - target->overlay_texture = 0; - } - - init = false; - - target->old_size = size; - } - if (col) - buffer = MEM_mallocN(sizeof(GLubyte) * size * size * 4, "load_tex"); - else - buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex"); - - pool = BKE_image_pool_new(); - - if (mtex->tex && mtex->tex->nodetree) { - /* has internal flag to detect it only does it once */ - ntreeTexBeginExecTree(mtex->tex->nodetree); - } - - LoadTexData data = { - .br = br, .vc = vc, .mtex = mtex, .buffer = buffer, .col = col, - .pool = pool, .size = size, .rotation = rotation, .radius = radius, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range(0, size, &data, load_tex_task_cb_ex, &settings); - - if (mtex->tex && mtex->tex->nodetree) - ntreeTexEndExecTree(mtex->tex->nodetree->execdata); - - if (pool) - BKE_image_pool_free(pool); - - if (!target->overlay_texture) - glGenTextures(1, &target->overlay_texture); - } - else { - size = target->old_size; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, target->overlay_texture); - - if (refresh) { - GLenum format = col ? GL_RGBA : GL_RED; - GLenum internalformat = col ? GL_RGBA8 : GL_R8; - - if (!init || (target->old_col != col)) { - glTexImage2D(GL_TEXTURE_2D, 0, internalformat, size, size, 0, format, GL_UNSIGNED_BYTE, buffer); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, format, GL_UNSIGNED_BYTE, buffer); - } - - if (buffer) - MEM_freeN(buffer); - - target->old_col = col; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - } - - BKE_paint_reset_overlay_invalid(invalid); - - return 1; + bool init; + TexSnapshot *target; + + MTex *mtex = (primary) ? &br->mtex : &br->mask_mtex; + eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); + GLubyte *buffer = NULL; + + int size; + bool refresh; + eOverlayControlFlags invalid = ((primary) ? + (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY) : + (overlay_flags & PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY)); + target = (primary) ? &primary_snap : &secondary_snap; + + refresh = !target->overlay_texture || (invalid != 0) || + !same_tex_snap(target, mtex, vc, col, zoom); + + init = (target->overlay_texture != 0); + + if (refresh) { + struct ImagePool *pool = NULL; + /* stencil is rotated later */ + const float rotation = (mtex->brush_map_mode != MTEX_MAP_MODE_STENCIL) ? -mtex->rot : 0.0f; + const float radius = BKE_brush_size_get(vc->scene, br) * zoom; + + make_tex_snap(target, vc, zoom); + + if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { + int s = BKE_brush_size_get(vc->scene, br); + int r = 1; + + for (s >>= 1; s > 0; s >>= 1) + r++; + + size = (1 << r); + + if (size < 256) + size = 256; + + if (size < target->old_size) + size = target->old_size; + } + else { + size = 512; + } + + if (target->old_size != size) { + if (target->overlay_texture) { + glDeleteTextures(1, &target->overlay_texture); + target->overlay_texture = 0; + } + + init = false; + + target->old_size = size; + } + if (col) + buffer = MEM_mallocN(sizeof(GLubyte) * size * size * 4, "load_tex"); + else + buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex"); + + pool = BKE_image_pool_new(); + + if (mtex->tex && mtex->tex->nodetree) { + /* has internal flag to detect it only does it once */ + ntreeTexBeginExecTree(mtex->tex->nodetree); + } + + LoadTexData data = { + .br = br, + .vc = vc, + .mtex = mtex, + .buffer = buffer, + .col = col, + .pool = pool, + .size = size, + .rotation = rotation, + .radius = radius, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, size, &data, load_tex_task_cb_ex, &settings); + + if (mtex->tex && mtex->tex->nodetree) + ntreeTexEndExecTree(mtex->tex->nodetree->execdata); + + if (pool) + BKE_image_pool_free(pool); + + if (!target->overlay_texture) + glGenTextures(1, &target->overlay_texture); + } + else { + size = target->old_size; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, target->overlay_texture); + + if (refresh) { + GLenum format = col ? GL_RGBA : GL_RED; + GLenum internalformat = col ? GL_RGBA8 : GL_R8; + + if (!init || (target->old_col != col)) { + glTexImage2D( + GL_TEXTURE_2D, 0, internalformat, size, size, 0, format, GL_UNSIGNED_BYTE, buffer); + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, format, GL_UNSIGNED_BYTE, buffer); + } + + if (buffer) + MEM_freeN(buffer); + + target->old_col = col; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } + + BKE_paint_reset_overlay_invalid(invalid); + + return 1; } -static void load_tex_cursor_task_cb( - void *__restrict userdata, - const int j, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void load_tex_cursor_task_cb(void *__restrict userdata, + const int j, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - LoadTexData *data = userdata; - Brush *br = data->br; + LoadTexData *data = userdata; + Brush *br = data->br; - GLubyte *buffer = data->buffer; + GLubyte *buffer = data->buffer; - const int size = data->size; + const int size = data->size; - for (int i = 0; i < size; i++) { - // largely duplicated from tex_strength + for (int i = 0; i < size; i++) { + // largely duplicated from tex_strength - const int index = j * size + i; - const float x = (((float)i / size) - 0.5f) * 2.0f; - const float y = (((float)j / size) - 0.5f) * 2.0f; - const float len = sqrtf(x * x + y * y); + const int index = j * size + i; + const float x = (((float)i / size) - 0.5f) * 2.0f; + const float y = (((float)j / size) - 0.5f) * 2.0f; + const float len = sqrtf(x * x + y * y); - if (len <= 1.0f) { - float avg = BKE_brush_curve_strength_clamped(br, len, 1.0f); /* Falloff curve */ + if (len <= 1.0f) { + float avg = BKE_brush_curve_strength_clamped(br, len, 1.0f); /* Falloff curve */ - buffer[index] = 255 - (GLubyte)(255 * avg); - } - else { - buffer[index] = 0; - } - } + buffer[index] = 255 - (GLubyte)(255 * avg); + } + else { + buffer[index] = 0; + } + } } static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) { - bool init; + bool init; - eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); - GLubyte *buffer = NULL; + eOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); + GLubyte *buffer = NULL; - int size; - const bool refresh = - !cursor_snap.overlay_texture || - (overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || - cursor_snap.zoom != zoom; + int size; + const bool refresh = !cursor_snap.overlay_texture || + (overlay_flags & PAINT_OVERLAY_INVALID_CURVE) || cursor_snap.zoom != zoom; - init = (cursor_snap.overlay_texture != 0); + init = (cursor_snap.overlay_texture != 0); - if (refresh) { - int s, r; + if (refresh) { + int s, r; - cursor_snap.zoom = zoom; + cursor_snap.zoom = zoom; - s = BKE_brush_size_get(vc->scene, br); - r = 1; + s = BKE_brush_size_get(vc->scene, br); + r = 1; - for (s >>= 1; s > 0; s >>= 1) - r++; + for (s >>= 1; s > 0; s >>= 1) + r++; - size = (1 << r); + size = (1 << r); - if (size < 256) - size = 256; + if (size < 256) + size = 256; - if (size < cursor_snap.size) - size = cursor_snap.size; + if (size < cursor_snap.size) + size = cursor_snap.size; - if (cursor_snap.size != size) { - if (cursor_snap.overlay_texture) { - glDeleteTextures(1, &cursor_snap.overlay_texture); - cursor_snap.overlay_texture = 0; - } + if (cursor_snap.size != size) { + if (cursor_snap.overlay_texture) { + glDeleteTextures(1, &cursor_snap.overlay_texture); + cursor_snap.overlay_texture = 0; + } - init = false; + init = false; - cursor_snap.size = size; - } - buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex"); + cursor_snap.size = size; + } + buffer = MEM_mallocN(sizeof(GLubyte) * size * size, "load_tex"); - curvemapping_initialize(br->curve); + curvemapping_initialize(br->curve); - LoadTexData data = { - .br = br, .buffer = buffer, .size = size, - }; + LoadTexData data = { + .br = br, + .buffer = buffer, + .size = size, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range(0, size, &data, load_tex_cursor_task_cb, &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, size, &data, load_tex_cursor_task_cb, &settings); - if (!cursor_snap.overlay_texture) - glGenTextures(1, &cursor_snap.overlay_texture); - } - else { - size = cursor_snap.size; - } + if (!cursor_snap.overlay_texture) + glGenTextures(1, &cursor_snap.overlay_texture); + } + else { + size = cursor_snap.size; + } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, cursor_snap.overlay_texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, cursor_snap.overlay_texture); - if (refresh) { - if (!init) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size, size, 0, GL_RED, GL_UNSIGNED_BYTE, buffer); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_RED, GL_UNSIGNED_BYTE, buffer); - } + if (refresh) { + if (!init) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size, size, 0, GL_RED, GL_UNSIGNED_BYTE, buffer); + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_RED, GL_UNSIGNED_BYTE, buffer); + } - if (buffer) - MEM_freeN(buffer); - } + if (buffer) + MEM_freeN(buffer); + } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - BKE_paint_reset_overlay_invalid(PAINT_OVERLAY_INVALID_CURVE); + BKE_paint_reset_overlay_invalid(PAINT_OVERLAY_INVALID_CURVE); - return 1; + return 1; } - - -static int project_brush_radius( - ViewContext *vc, - float radius, - const float location[3]) +static int project_brush_radius(ViewContext *vc, float radius, const float location[3]) { - float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; - - ED_view3d_global_to_vector(vc->rv3d, location, view); - - /* create a vector that is not orthogonal to view */ - - if (fabsf(view[0]) < 0.1f) { - nonortho[0] = view[0] + 1.0f; - nonortho[1] = view[1]; - nonortho[2] = view[2]; - } - else if (fabsf(view[1]) < 0.1f) { - nonortho[0] = view[0]; - nonortho[1] = view[1] + 1.0f; - nonortho[2] = view[2]; - } - else { - nonortho[0] = view[0]; - nonortho[1] = view[1]; - nonortho[2] = view[2] + 1.0f; - } - - /* get a vector in the plane of the view */ - cross_v3_v3v3(ortho, nonortho, view); - normalize_v3(ortho); - - /* make a point on the surface of the brush tangent to the view */ - mul_v3_fl(ortho, radius); - add_v3_v3v3(offset, location, ortho); - - /* project the center of the brush, and the tangent point to the view onto the screen */ - if ((ED_view3d_project_float_global(vc->ar, location, p1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) && - (ED_view3d_project_float_global(vc->ar, offset, p2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK)) - { - /* the distance between these points is the size of the projected brush in pixels */ - return len_v2v2(p1, p2); - } - else { - BLI_assert(0); /* assert because the code that sets up the vectors should disallow this */ - return 0; - } + float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2]; + + ED_view3d_global_to_vector(vc->rv3d, location, view); + + /* create a vector that is not orthogonal to view */ + + if (fabsf(view[0]) < 0.1f) { + nonortho[0] = view[0] + 1.0f; + nonortho[1] = view[1]; + nonortho[2] = view[2]; + } + else if (fabsf(view[1]) < 0.1f) { + nonortho[0] = view[0]; + nonortho[1] = view[1] + 1.0f; + nonortho[2] = view[2]; + } + else { + nonortho[0] = view[0]; + nonortho[1] = view[1]; + nonortho[2] = view[2] + 1.0f; + } + + /* get a vector in the plane of the view */ + cross_v3_v3v3(ortho, nonortho, view); + normalize_v3(ortho); + + /* make a point on the surface of the brush tangent to the view */ + mul_v3_fl(ortho, radius); + add_v3_v3v3(offset, location, ortho); + + /* project the center of the brush, and the tangent point to the view onto the screen */ + if ((ED_view3d_project_float_global(vc->ar, location, p1, V3D_PROJ_TEST_NOP) == + V3D_PROJ_RET_OK) && + (ED_view3d_project_float_global(vc->ar, offset, p2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK)) { + /* the distance between these points is the size of the projected brush in pixels */ + return len_v2v2(p1, p2); + } + else { + BLI_assert(0); /* assert because the code that sets up the vectors should disallow this */ + return 0; + } } -static bool sculpt_get_brush_geometry( - bContext *C, ViewContext *vc, - int x, int y, int *pixel_radius, - float location[3], UnifiedPaintSettings *ups) +static bool sculpt_get_brush_geometry(bContext *C, + ViewContext *vc, + int x, + int y, + int *pixel_radius, + float location[3], + UnifiedPaintSettings *ups) { - Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - float mouse[2]; - bool hit = false; - - mouse[0] = x; - mouse[1] = y; - - if (vc->obact->sculpt && vc->obact->sculpt->pbvh) { - if (!ups->stroke_active) { - hit = sculpt_stroke_get_location(C, location, mouse); - } - else { - hit = ups->last_hit; - copy_v3_v3(location, ups->last_location); - } - } - - if (hit) { - Brush *brush = BKE_paint_brush(paint); - - *pixel_radius = project_brush_radius( - vc, BKE_brush_unprojected_radius_get(scene, brush), location); - - if (*pixel_radius == 0) - *pixel_radius = BKE_brush_size_get(scene, brush); - - mul_m4_v3(vc->obact->obmat, location); - } - else { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - *pixel_radius = BKE_brush_size_get(scene, brush); - } - - return hit; + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + float mouse[2]; + bool hit = false; + + mouse[0] = x; + mouse[1] = y; + + if (vc->obact->sculpt && vc->obact->sculpt->pbvh) { + if (!ups->stroke_active) { + hit = sculpt_stroke_get_location(C, location, mouse); + } + else { + hit = ups->last_hit; + copy_v3_v3(location, ups->last_location); + } + } + + if (hit) { + Brush *brush = BKE_paint_brush(paint); + + *pixel_radius = project_brush_radius( + vc, BKE_brush_unprojected_radius_get(scene, brush), location); + + if (*pixel_radius == 0) + *pixel_radius = BKE_brush_size_get(scene, brush); + + mul_m4_v3(vc->obact->obmat, location); + } + else { + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + *pixel_radius = BKE_brush_size_get(scene, brush); + } + + return hit; } /* Draw an overlay that shows what effect the brush's texture will * have on brush strength */ -static void paint_draw_tex_overlay( - UnifiedPaintSettings *ups, Brush *brush, - ViewContext *vc, int x, int y, float zoom, bool col, bool primary) +static void paint_draw_tex_overlay(UnifiedPaintSettings *ups, + Brush *brush, + ViewContext *vc, + int x, + int y, + float zoom, + bool col, + bool primary) { - rctf quad; - /* check for overlay mode */ - - MTex *mtex = (primary) ? &brush->mtex : &brush->mask_mtex; - bool valid = ( - (primary) ? - (brush->overlay_flags & BRUSH_OVERLAY_PRIMARY) != 0 : - (brush->overlay_flags & BRUSH_OVERLAY_SECONDARY) != 0); - int overlay_alpha = (primary) ? brush->texture_overlay_alpha : brush->mask_overlay_alpha; - - if (!(mtex->tex) || !((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) || - (valid && - ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) - { - return; - } - - if (load_tex(brush, vc, zoom, col, primary)) { - GPU_blend(true); - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDepthMask(GL_FALSE); - glDepthFunc(GL_ALWAYS); - - if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { - GPU_matrix_push(); - - /* brush rotation */ - GPU_matrix_translate_2f(x, y); - GPU_matrix_rotate_2d(-RAD2DEGF(primary ? ups->brush_rotation : ups->brush_rotation_sec)); - GPU_matrix_translate_2f(-x, -y); - - /* scale based on tablet pressure */ - if (primary && ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) { - const float scale = ups->size_pressure_value; - GPU_matrix_translate_2f(x, y); - GPU_matrix_scale_2f(scale, scale); - GPU_matrix_translate_2f(-x, -y); - } - - if (ups->draw_anchored) { - quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size; - quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size; - quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size; - quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; - } - else { - const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; - quad.xmin = x - radius; - quad.ymin = y - radius; - quad.xmax = x + radius; - quad.ymax = y + radius; - } - } - else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { - quad.xmin = 0; - quad.ymin = 0; - quad.xmax = BLI_rcti_size_x(&vc->ar->winrct); - quad.ymax = BLI_rcti_size_y(&vc->ar->winrct); - } - /* Stencil code goes here */ - else { - if (primary) { - quad.xmin = -brush->stencil_dimension[0]; - quad.ymin = -brush->stencil_dimension[1]; - quad.xmax = brush->stencil_dimension[0]; - quad.ymax = brush->stencil_dimension[1]; - } - else { - quad.xmin = -brush->mask_stencil_dimension[0]; - quad.ymin = -brush->mask_stencil_dimension[1]; - quad.xmax = brush->mask_stencil_dimension[0]; - quad.ymax = brush->mask_stencil_dimension[1]; - } - GPU_matrix_push(); - if (primary) - GPU_matrix_translate_2fv(brush->stencil_pos); - else - GPU_matrix_translate_2fv(brush->mask_stencil_pos); - GPU_matrix_rotate_2d(RAD2DEGF(mtex->rot)); - } - - /* set quad color. Colored overlay does not get blending */ - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - if (col) { - immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); - immUniformColor4f(1.0f, 1.0f, 1.0f, overlay_alpha * 0.01f); - } - else { - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR); - immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, overlay_alpha * 0.01f); - } - - /* draw textured quad */ - immUniform1i("image", 0); - - immBegin(GPU_PRIM_TRI_FAN, 4); - immAttr2f(texCoord, 0.0f, 0.0f); - immVertex2f(pos, quad.xmin, quad.ymin); - immAttr2f(texCoord, 1.0f, 0.0f); - immVertex2f(pos, quad.xmax, quad.ymin); - immAttr2f(texCoord, 1.0f, 1.0f); - immVertex2f(pos, quad.xmax, quad.ymax); - immAttr2f(texCoord, 0.0f, 1.0f); - immVertex2f(pos, quad.xmin, quad.ymax); - immEnd(); - - immUnbindProgram(); - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); - - if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) { - GPU_matrix_pop(); - } - } + rctf quad; + /* check for overlay mode */ + + MTex *mtex = (primary) ? &brush->mtex : &brush->mask_mtex; + bool valid = ((primary) ? (brush->overlay_flags & BRUSH_OVERLAY_PRIMARY) != 0 : + (brush->overlay_flags & BRUSH_OVERLAY_SECONDARY) != 0); + int overlay_alpha = (primary) ? brush->texture_overlay_alpha : brush->mask_overlay_alpha; + + if (!(mtex->tex) || + !((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) || + (valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) { + return; + } + + if (load_tex(brush, vc, zoom, col, primary)) { + GPU_blend(true); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + glDepthFunc(GL_ALWAYS); + + if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { + GPU_matrix_push(); + + /* brush rotation */ + GPU_matrix_translate_2f(x, y); + GPU_matrix_rotate_2d(-RAD2DEGF(primary ? ups->brush_rotation : ups->brush_rotation_sec)); + GPU_matrix_translate_2f(-x, -y); + + /* scale based on tablet pressure */ + if (primary && ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) { + const float scale = ups->size_pressure_value; + GPU_matrix_translate_2f(x, y); + GPU_matrix_scale_2f(scale, scale); + GPU_matrix_translate_2f(-x, -y); + } + + if (ups->draw_anchored) { + quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size; + quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size; + quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size; + quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; + } + else { + const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; + quad.xmin = x - radius; + quad.ymin = y - radius; + quad.xmax = x + radius; + quad.ymax = y + radius; + } + } + else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) { + quad.xmin = 0; + quad.ymin = 0; + quad.xmax = BLI_rcti_size_x(&vc->ar->winrct); + quad.ymax = BLI_rcti_size_y(&vc->ar->winrct); + } + /* Stencil code goes here */ + else { + if (primary) { + quad.xmin = -brush->stencil_dimension[0]; + quad.ymin = -brush->stencil_dimension[1]; + quad.xmax = brush->stencil_dimension[0]; + quad.ymax = brush->stencil_dimension[1]; + } + else { + quad.xmin = -brush->mask_stencil_dimension[0]; + quad.ymin = -brush->mask_stencil_dimension[1]; + quad.xmax = brush->mask_stencil_dimension[0]; + quad.ymax = brush->mask_stencil_dimension[1]; + } + GPU_matrix_push(); + if (primary) + GPU_matrix_translate_2fv(brush->stencil_pos); + else + GPU_matrix_translate_2fv(brush->mask_stencil_pos); + GPU_matrix_rotate_2d(RAD2DEGF(mtex->rot)); + } + + /* set quad color. Colored overlay does not get blending */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + if (col) { + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); + immUniformColor4f(1.0f, 1.0f, 1.0f, overlay_alpha * 0.01f); + } + else { + GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR); + immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, overlay_alpha * 0.01f); + } + + /* draw textured quad */ + immUniform1i("image", 0); + + immBegin(GPU_PRIM_TRI_FAN, 4); + immAttr2f(texCoord, 0.0f, 0.0f); + immVertex2f(pos, quad.xmin, quad.ymin); + immAttr2f(texCoord, 1.0f, 0.0f); + immVertex2f(pos, quad.xmax, quad.ymin); + immAttr2f(texCoord, 1.0f, 1.0f); + immVertex2f(pos, quad.xmax, quad.ymax); + immAttr2f(texCoord, 0.0f, 1.0f); + immVertex2f(pos, quad.xmin, quad.ymax); + immEnd(); + + immUnbindProgram(); + GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); + + if (ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_VIEW)) { + GPU_matrix_pop(); + } + } } /* Draw an overlay that shows what effect the brush's texture will * have on brush strength */ static void paint_draw_cursor_overlay( - UnifiedPaintSettings *ups, Brush *brush, - ViewContext *vc, int x, int y, float zoom) + UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom) { - rctf quad; - /* check for overlay mode */ - - if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) { - return; - } - - if (load_tex_cursor(brush, vc, zoom)) { - bool do_pop = false; - float center[2]; - GPU_blend(true); - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDepthMask(GL_FALSE); - glDepthFunc(GL_ALWAYS); - - if (ups->draw_anchored) { - copy_v2_v2(center, ups->anchored_initial_mouse); - quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size; - quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size; - quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size; - quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; - } - else { - const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; - center[0] = x; - center[1] = y; - - quad.xmin = x - radius; - quad.ymin = y - radius; - quad.xmax = x + radius; - quad.ymax = y + radius; - } - - /* scale based on tablet pressure */ - if (ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) { - do_pop = true; - GPU_matrix_push(); - GPU_matrix_translate_2fv(center); - GPU_matrix_scale_1f(ups->size_pressure_value); - GPU_matrix_translate_2f(-center[0], -center[1]); - } - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR); - - immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, brush->cursor_overlay_alpha * 0.01f); - - /* draw textured quad */ - - /* draw textured quad */ - immUniform1i("image", 0); - - immBegin(GPU_PRIM_TRI_FAN, 4); - immAttr2f(texCoord, 0.0f, 0.0f); - immVertex2f(pos, quad.xmin, quad.ymin); - immAttr2f(texCoord, 1.0f, 0.0f); - immVertex2f(pos, quad.xmax, quad.ymin); - immAttr2f(texCoord, 1.0f, 1.0f); - immVertex2f(pos, quad.xmax, quad.ymax); - immAttr2f(texCoord, 0.0f, 1.0f); - immVertex2f(pos, quad.xmin, quad.ymax); - immEnd(); - - immUnbindProgram(); - - GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); - - if (do_pop) - GPU_matrix_pop(); - } + rctf quad; + /* check for overlay mode */ + + if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) { + return; + } + + if (load_tex_cursor(brush, vc, zoom)) { + bool do_pop = false; + float center[2]; + GPU_blend(true); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); + glDepthFunc(GL_ALWAYS); + + if (ups->draw_anchored) { + copy_v2_v2(center, ups->anchored_initial_mouse); + quad.xmin = ups->anchored_initial_mouse[0] - ups->anchored_size; + quad.ymin = ups->anchored_initial_mouse[1] - ups->anchored_size; + quad.xmax = ups->anchored_initial_mouse[0] + ups->anchored_size; + quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; + } + else { + const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; + center[0] = x; + center[1] = y; + + quad.xmin = x - radius; + quad.ymin = y - radius; + quad.xmax = x + radius; + quad.ymax = y + radius; + } + + /* scale based on tablet pressure */ + if (ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) { + do_pop = true; + GPU_matrix_push(); + GPU_matrix_translate_2fv(center); + GPU_matrix_scale_1f(ups->size_pressure_value); + GPU_matrix_translate_2f(-center[0], -center[1]); + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + GPU_blend_set_func(GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_ALPHA_COLOR); + + immUniformColor3fvAlpha(U.sculpt_paint_overlay_col, brush->cursor_overlay_alpha * 0.01f); + + /* draw textured quad */ + + /* draw textured quad */ + immUniform1i("image", 0); + + immBegin(GPU_PRIM_TRI_FAN, 4); + immAttr2f(texCoord, 0.0f, 0.0f); + immVertex2f(pos, quad.xmin, quad.ymin); + immAttr2f(texCoord, 1.0f, 0.0f); + immVertex2f(pos, quad.xmax, quad.ymin); + immAttr2f(texCoord, 1.0f, 1.0f); + immVertex2f(pos, quad.xmax, quad.ymax); + immAttr2f(texCoord, 0.0f, 1.0f); + immVertex2f(pos, quad.xmin, quad.ymax); + immEnd(); + + immUnbindProgram(); + + GPU_blend_set_func(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); + + if (do_pop) + GPU_matrix_pop(); + } } -static void paint_draw_alpha_overlay( - UnifiedPaintSettings *ups, Brush *brush, - ViewContext *vc, int x, int y, float zoom, ePaintMode mode) +static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, + Brush *brush, + ViewContext *vc, + int x, + int y, + float zoom, + ePaintMode mode) { - /* Color means that primary brush texture is colored and - * secondary is used for alpha/mask control. */ - bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX); - eOverlayControlFlags flags = BKE_paint_get_overlay_flags(); - gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT); - - /* Translate to region. */ - GPU_matrix_push(); - GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin); - x -= vc->ar->winrct.xmin; - y -= vc->ar->winrct.ymin; - - /* coloured overlay should be drawn separately */ - if (col) { - if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true); - if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false); - if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) - paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); - } - else { - if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) - paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true); - if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) - paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); - } - - GPU_matrix_pop(); - gpuPopAttr(); + /* Color means that primary brush texture is colored and + * secondary is used for alpha/mask control. */ + bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX); + eOverlayControlFlags flags = BKE_paint_get_overlay_flags(); + gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT); + + /* Translate to region. */ + GPU_matrix_push(); + GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin); + x -= vc->ar->winrct.xmin; + y -= vc->ar->winrct.ymin; + + /* coloured overlay should be drawn separately */ + if (col) { + if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY)) + paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, true, true); + if (!(flags & PAINT_OVERLAY_OVERRIDE_SECONDARY)) + paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, false); + if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) + paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); + } + else { + if (!(flags & PAINT_OVERLAY_OVERRIDE_PRIMARY) && (mode != PAINT_MODE_WEIGHT)) + paint_draw_tex_overlay(ups, brush, vc, x, y, zoom, false, true); + if (!(flags & PAINT_OVERLAY_OVERRIDE_CURSOR)) + paint_draw_cursor_overlay(ups, brush, vc, x, y, zoom); + } + + GPU_matrix_pop(); + gpuPopAttr(); } - BLI_INLINE void draw_tri_point( - unsigned int pos, float sel_col[4], float pivot_col[4], - float *co, float width, bool selected) + unsigned int pos, float sel_col[4], float pivot_col[4], float *co, float width, bool selected) { - immUniformColor4fv(selected ? sel_col : pivot_col); - - GPU_line_width(3.0f); - - float w = width / 2.0f; - float tri[3][2] = { - {co[0], co[1] + w}, - {co[0] - w, co[1] - w}, - {co[0] + w, co[1] - w}, - }; - - immBegin(GPU_PRIM_LINE_LOOP, 3); - immVertex2fv(pos, tri[0]); - immVertex2fv(pos, tri[1]); - immVertex2fv(pos, tri[2]); - immEnd(); - - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); - GPU_line_width(1.0f); - - immBegin(GPU_PRIM_LINE_LOOP, 3); - immVertex2fv(pos, tri[0]); - immVertex2fv(pos, tri[1]); - immVertex2fv(pos, tri[2]); - immEnd(); + immUniformColor4fv(selected ? sel_col : pivot_col); + + GPU_line_width(3.0f); + + float w = width / 2.0f; + float tri[3][2] = { + {co[0], co[1] + w}, + {co[0] - w, co[1] - w}, + {co[0] + w, co[1] - w}, + }; + + immBegin(GPU_PRIM_LINE_LOOP, 3); + immVertex2fv(pos, tri[0]); + immVertex2fv(pos, tri[1]); + immVertex2fv(pos, tri[2]); + immEnd(); + + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + GPU_line_width(1.0f); + + immBegin(GPU_PRIM_LINE_LOOP, 3); + immVertex2fv(pos, tri[0]); + immVertex2fv(pos, tri[1]); + immVertex2fv(pos, tri[2]); + immEnd(); } BLI_INLINE void draw_rect_point( - unsigned int pos, float sel_col[4], float handle_col[4], - float *co, float width, bool selected) + unsigned int pos, float sel_col[4], float handle_col[4], float *co, float width, bool selected) { - immUniformColor4fv(selected ? sel_col : handle_col); + immUniformColor4fv(selected ? sel_col : handle_col); - GPU_line_width(3.0f); + GPU_line_width(3.0f); - float w = width / 2.0f; - float minx = co[0] - w; - float miny = co[1] - w; - float maxx = co[0] + w; - float maxy = co[1] + w; + float w = width / 2.0f; + float minx = co[0] - w; + float miny = co[1] - w; + float maxx = co[0] + w; + float maxy = co[1] + w; - imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); + imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); - GPU_line_width(1.0f); + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + GPU_line_width(1.0f); - imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); + imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); } - BLI_INLINE void draw_bezier_handle_lines(unsigned int pos, float sel_col[4], BezTriple *bez) { - immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); - GPU_line_width(3.0f); - - immBegin(GPU_PRIM_LINE_STRIP, 3); - immVertex2fv(pos, bez->vec[0]); - immVertex2fv(pos, bez->vec[1]); - immVertex2fv(pos, bez->vec[2]); - immEnd(); - - GPU_line_width(1.0f); - - if (bez->f1 || bez->f2) { - immUniformColor4fv(sel_col); - } - else { - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); - } - immBegin(GPU_PRIM_LINES, 2); - immVertex2fv(pos, bez->vec[0]); - immVertex2fv(pos, bez->vec[1]); - immEnd(); - - if (bez->f3 || bez->f2) { - immUniformColor4fv(sel_col); - } - else { - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); - } - immBegin(GPU_PRIM_LINES, 2); - immVertex2fv(pos, bez->vec[1]); - immVertex2fv(pos, bez->vec[2]); - immEnd(); + immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); + GPU_line_width(3.0f); + + immBegin(GPU_PRIM_LINE_STRIP, 3); + immVertex2fv(pos, bez->vec[0]); + immVertex2fv(pos, bez->vec[1]); + immVertex2fv(pos, bez->vec[2]); + immEnd(); + + GPU_line_width(1.0f); + + if (bez->f1 || bez->f2) { + immUniformColor4fv(sel_col); + } + else { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + } + immBegin(GPU_PRIM_LINES, 2); + immVertex2fv(pos, bez->vec[0]); + immVertex2fv(pos, bez->vec[1]); + immEnd(); + + if (bez->f3 || bez->f2) { + immUniformColor4fv(sel_col); + } + else { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + } + immBegin(GPU_PRIM_LINES, 2); + immVertex2fv(pos, bez->vec[1]); + immVertex2fv(pos, bez->vec[2]); + immEnd(); } static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc) { - GPU_matrix_push(); - GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin); - - if (brush->paint_curve && brush->paint_curve->points) { - int i; - PaintCurve *pc = brush->paint_curve; - PaintCurvePoint *cp = pc->points; - - GPU_line_smooth(true); - GPU_blend(true); - - /* draw the bezier handles and the curve segment between the current and next point */ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - float selec_col[4], handle_col[4], pivot_col[4]; - UI_GetThemeColor4fv(TH_VERTEX_SELECT, selec_col); - UI_GetThemeColor4fv(TH_PAINT_CURVE_HANDLE, handle_col); - UI_GetThemeColor4fv(TH_PAINT_CURVE_PIVOT, pivot_col); - - for (i = 0; i < pc->tot_points - 1; i++, cp++) { - int j; - PaintCurvePoint *cp_next = cp + 1; - float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; - /* use color coding to distinguish handles vs curve segments */ - draw_bezier_handle_lines(pos, selec_col, &cp->bez); - draw_tri_point(pos, selec_col, pivot_col, &cp->bez.vec[1][0], 10.0f, cp->bez.f2); - draw_rect_point(pos, selec_col, handle_col, &cp->bez.vec[0][0], 8.0f, cp->bez.f1 || cp->bez.f2); - draw_rect_point(pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2); - - for (j = 0; j < 2; j++) - BKE_curve_forward_diff_bezier( - cp->bez.vec[1][j], - cp->bez.vec[2][j], - cp_next->bez.vec[0][j], - cp_next->bez.vec[1][j], - data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); - - float (*v)[2] = (float(*)[2])data; - - immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); - GPU_line_width(3.0f); - immBegin(GPU_PRIM_LINE_STRIP, PAINT_CURVE_NUM_SEGMENTS + 1); - for (j = 0; j <= PAINT_CURVE_NUM_SEGMENTS; j++) { - immVertex2fv(pos, v[j]); - } - immEnd(); - - immUniformColor4f(0.9f, 0.9f, 1.0f, 0.5f); - GPU_line_width(1.0f); - immBegin(GPU_PRIM_LINE_STRIP, PAINT_CURVE_NUM_SEGMENTS + 1); - for (j = 0; j <= PAINT_CURVE_NUM_SEGMENTS; j++) { - immVertex2fv(pos, v[j]); - } - immEnd(); - } - - /* draw last line segment */ - draw_bezier_handle_lines(pos, selec_col, &cp->bez); - draw_tri_point(pos, selec_col, pivot_col, &cp->bez.vec[1][0], 10.0f, cp->bez.f2); - draw_rect_point(pos, selec_col, handle_col, &cp->bez.vec[0][0], 8.0f, cp->bez.f1 || cp->bez.f2); - draw_rect_point(pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2); - - GPU_blend(false); - GPU_line_smooth(false); - - immUnbindProgram(); - } - GPU_matrix_pop(); + GPU_matrix_push(); + GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin); + + if (brush->paint_curve && brush->paint_curve->points) { + int i; + PaintCurve *pc = brush->paint_curve; + PaintCurvePoint *cp = pc->points; + + GPU_line_smooth(true); + GPU_blend(true); + + /* draw the bezier handles and the curve segment between the current and next point */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + float selec_col[4], handle_col[4], pivot_col[4]; + UI_GetThemeColor4fv(TH_VERTEX_SELECT, selec_col); + UI_GetThemeColor4fv(TH_PAINT_CURVE_HANDLE, handle_col); + UI_GetThemeColor4fv(TH_PAINT_CURVE_PIVOT, pivot_col); + + for (i = 0; i < pc->tot_points - 1; i++, cp++) { + int j; + PaintCurvePoint *cp_next = cp + 1; + float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + /* use color coding to distinguish handles vs curve segments */ + draw_bezier_handle_lines(pos, selec_col, &cp->bez); + draw_tri_point(pos, selec_col, pivot_col, &cp->bez.vec[1][0], 10.0f, cp->bez.f2); + draw_rect_point( + pos, selec_col, handle_col, &cp->bez.vec[0][0], 8.0f, cp->bez.f1 || cp->bez.f2); + draw_rect_point( + pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2); + + for (j = 0; j < 2; j++) + BKE_curve_forward_diff_bezier(cp->bez.vec[1][j], + cp->bez.vec[2][j], + cp_next->bez.vec[0][j], + cp_next->bez.vec[1][j], + data + j, + PAINT_CURVE_NUM_SEGMENTS, + sizeof(float[2])); + + float(*v)[2] = (float(*)[2])data; + + immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); + GPU_line_width(3.0f); + immBegin(GPU_PRIM_LINE_STRIP, PAINT_CURVE_NUM_SEGMENTS + 1); + for (j = 0; j <= PAINT_CURVE_NUM_SEGMENTS; j++) { + immVertex2fv(pos, v[j]); + } + immEnd(); + + immUniformColor4f(0.9f, 0.9f, 1.0f, 0.5f); + GPU_line_width(1.0f); + immBegin(GPU_PRIM_LINE_STRIP, PAINT_CURVE_NUM_SEGMENTS + 1); + for (j = 0; j <= PAINT_CURVE_NUM_SEGMENTS; j++) { + immVertex2fv(pos, v[j]); + } + immEnd(); + } + + /* draw last line segment */ + draw_bezier_handle_lines(pos, selec_col, &cp->bez); + draw_tri_point(pos, selec_col, pivot_col, &cp->bez.vec[1][0], 10.0f, cp->bez.f2); + draw_rect_point( + pos, selec_col, handle_col, &cp->bez.vec[0][0], 8.0f, cp->bez.f1 || cp->bez.f2); + draw_rect_point( + pos, selec_col, handle_col, &cp->bez.vec[2][0], 8.0f, cp->bez.f3 || cp->bez.f2); + + GPU_blend(false); + GPU_line_smooth(false); + + immUnbindProgram(); + } + GPU_matrix_pop(); } /* Special actions taken when paint cursor goes over mesh */ /* TODO: sculpt only for now */ -static void paint_cursor_on_hit( - UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, - const float location[3]) +static void paint_cursor_on_hit(UnifiedPaintSettings *ups, + Brush *brush, + ViewContext *vc, + const float location[3]) { - float unprojected_radius, projected_radius; - - /* update the brush's cached 3D radius */ - if (!BKE_brush_use_locked_size(vc->scene, brush)) { - /* get 2D brush radius */ - if (ups->draw_anchored) - projected_radius = ups->anchored_size; - else { - if (brush->flag & BRUSH_ANCHORED) - projected_radius = 8; - else - projected_radius = BKE_brush_size_get(vc->scene, brush); - } - - /* convert brush radius from 2D to 3D */ - unprojected_radius = paint_calc_object_space_radius( - vc, location, projected_radius); - - /* scale 3D brush radius by pressure */ - if (ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) - unprojected_radius *= ups->size_pressure_value; - - /* set cached value in either Brush or UnifiedPaintSettings */ - BKE_brush_unprojected_radius_set(vc->scene, brush, unprojected_radius); - } + float unprojected_radius, projected_radius; + + /* update the brush's cached 3D radius */ + if (!BKE_brush_use_locked_size(vc->scene, brush)) { + /* get 2D brush radius */ + if (ups->draw_anchored) + projected_radius = ups->anchored_size; + else { + if (brush->flag & BRUSH_ANCHORED) + projected_radius = 8; + else + projected_radius = BKE_brush_size_get(vc->scene, brush); + } + + /* convert brush radius from 2D to 3D */ + unprojected_radius = paint_calc_object_space_radius(vc, location, projected_radius); + + /* scale 3D brush radius by pressure */ + if (ups->stroke_active && BKE_brush_use_size_pressure(vc->scene, brush)) + unprojected_radius *= ups->size_pressure_value; + + /* set cached value in either Brush or UnifiedPaintSettings */ + BKE_brush_unprojected_radius_set(vc->scene, brush, unprojected_radius); + } } static bool ommit_cursor_drawing(Paint *paint, ePaintMode mode, Brush *brush) { - if (paint->flags & PAINT_SHOW_BRUSH) { - if (ELEM(mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) && brush->imagepaint_tool == PAINT_TOOL_FILL) { - return true; - } - return false; - } - return true; + if (paint->flags & PAINT_SHOW_BRUSH) { + if (ELEM(mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) && + brush->imagepaint_tool == PAINT_TOOL_FILL) { + return true; + } + return false; + } + return true; } static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - - /* check that brush drawing is enabled */ - if (ommit_cursor_drawing(paint, mode, brush)) - return; - - /* can't use stroke vc here because this will be called during - * mouse over too, not just during a stroke */ - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); - - if (vc.rv3d && (vc.rv3d->rflag & RV3D_NAVIGATING)) { - return; - } - - /* skip everything and draw brush here */ - if (brush->flag & BRUSH_CURVE) { - paint_draw_curve_cursor(brush, &vc); - return; - } - - float zoomx, zoomy; - get_imapaint_zoom(C, &zoomx, &zoomy); - zoomx = max_ff(zoomx, zoomy); - - /* set various defaults */ - const float *outline_col = brush->add_col; - const float outline_alpha = 0.5f; - float translation[2] = { x, y }; - float final_radius = (BKE_brush_size_get(scene, brush) * zoomx); - - /* don't calculate rake angles while a stroke is active because the rake variables are global - * and we may get interference with the stroke itself. - * For line strokes, such interference is visible */ - if (!ups->stroke_active) { - paint_calculate_rake_rotation(ups, brush, translation); - } - - /* draw overlay */ - paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); - - /* TODO: as sculpt and other paint modes are unified, this - * special mode of drawing will go away */ - if ((mode == PAINT_MODE_SCULPT) && vc.obact->sculpt) { - float location[3]; - int pixel_radius; - - /* test if brush is over the mesh */ - bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); - - if (BKE_brush_use_locked_size(scene, brush)) - BKE_brush_size_set(scene, brush, pixel_radius); - - /* check if brush is subtracting, use different color then */ - /* TODO: no way currently to know state of pen flip or - * invert key modifier without starting a stroke */ - if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && - BKE_brush_sculpt_has_secondary_color(brush)) - { - outline_col = brush->sub_col; - } - - /* only do if brush is over the mesh */ - if (hit) - paint_cursor_on_hit(ups, brush, &vc, location); - } - - if (ups->draw_anchored) { - final_radius = ups->anchored_size; - copy_v2_fl2(translation, - ups->anchored_initial_mouse[0] + ar->winrct.xmin, - ups->anchored_initial_mouse[1] + ar->winrct.ymin); - } - - /* make lines pretty */ - GPU_line_width(1.0f); - GPU_blend(true); /* TODO: also set blend mode? */ - GPU_line_smooth(true); - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - /* set brush color */ - immUniformColor3fvAlpha(outline_col, outline_alpha); - - /* draw brush outline */ - if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { - /* inner at full alpha */ - imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); - /* outer at half alpha */ - immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); - } - imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); - - immUnbindProgram(); - - /* restore GL state */ - GPU_blend(false); - GPU_line_smooth(false); + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + + /* check that brush drawing is enabled */ + if (ommit_cursor_drawing(paint, mode, brush)) + return; + + /* can't use stroke vc here because this will be called during + * mouse over too, not just during a stroke */ + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); + + if (vc.rv3d && (vc.rv3d->rflag & RV3D_NAVIGATING)) { + return; + } + + /* skip everything and draw brush here */ + if (brush->flag & BRUSH_CURVE) { + paint_draw_curve_cursor(brush, &vc); + return; + } + + float zoomx, zoomy; + get_imapaint_zoom(C, &zoomx, &zoomy); + zoomx = max_ff(zoomx, zoomy); + + /* set various defaults */ + const float *outline_col = brush->add_col; + const float outline_alpha = 0.5f; + float translation[2] = {x, y}; + float final_radius = (BKE_brush_size_get(scene, brush) * zoomx); + + /* don't calculate rake angles while a stroke is active because the rake variables are global + * and we may get interference with the stroke itself. + * For line strokes, such interference is visible */ + if (!ups->stroke_active) { + paint_calculate_rake_rotation(ups, brush, translation); + } + + /* draw overlay */ + paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); + + /* TODO: as sculpt and other paint modes are unified, this + * special mode of drawing will go away */ + if ((mode == PAINT_MODE_SCULPT) && vc.obact->sculpt) { + float location[3]; + int pixel_radius; + + /* test if brush is over the mesh */ + bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups); + + if (BKE_brush_use_locked_size(scene, brush)) + BKE_brush_size_set(scene, brush, pixel_radius); + + /* check if brush is subtracting, use different color then */ + /* TODO: no way currently to know state of pen flip or + * invert key modifier without starting a stroke */ + if (((ups->draw_inverted == 0) ^ ((brush->flag & BRUSH_DIR_IN) == 0)) && + BKE_brush_sculpt_has_secondary_color(brush)) { + outline_col = brush->sub_col; + } + + /* only do if brush is over the mesh */ + if (hit) + paint_cursor_on_hit(ups, brush, &vc, location); + } + + if (ups->draw_anchored) { + final_radius = ups->anchored_size; + copy_v2_fl2(translation, + ups->anchored_initial_mouse[0] + ar->winrct.xmin, + ups->anchored_initial_mouse[1] + ar->winrct.ymin); + } + + /* make lines pretty */ + GPU_line_width(1.0f); + GPU_blend(true); /* TODO: also set blend mode? */ + GPU_line_smooth(true); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* set brush color */ + immUniformColor3fvAlpha(outline_col, outline_alpha); + + /* draw brush outline */ + if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) { + /* inner at full alpha */ + imm_draw_circle_wire_2d( + pos, translation[0], translation[1], final_radius * ups->size_pressure_value, 40); + /* outer at half alpha */ + immUniformColor3fvAlpha(outline_col, outline_alpha * 0.5f); + } + imm_draw_circle_wire_2d(pos, translation[0], translation[1], final_radius, 40); + + immUnbindProgram(); + + /* restore GL state */ + GPU_blend(false); + GPU_line_smooth(false); } /* Public API */ void paint_cursor_start(bContext *C, bool (*poll)(bContext *C)) { - Paint *p = BKE_paint_get_active_from_context(C); - - if (p && !p->paint_cursor) { - p->paint_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - poll, - paint_draw_cursor, - NULL); - } - - /* invalidate the paint cursors */ - BKE_paint_invalidate_overlay_all(); + Paint *p = BKE_paint_get_active_from_context(C); + + if (p && !p->paint_cursor) { + p->paint_cursor = WM_paint_cursor_activate( + CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, poll, paint_draw_cursor, NULL); + } + + /* invalidate the paint cursors */ + BKE_paint_invalidate_overlay_all(); } void paint_cursor_start_explicit(Paint *p, wmWindowManager *wm, bool (*poll)(bContext *C)) { - if (p && !p->paint_cursor) { - p->paint_cursor = WM_paint_cursor_activate( - wm, - SPACE_TYPE_ANY, RGN_TYPE_ANY, - poll, - paint_draw_cursor, - NULL); - } + if (p && !p->paint_cursor) { + p->paint_cursor = WM_paint_cursor_activate( + wm, SPACE_TYPE_ANY, RGN_TYPE_ANY, poll, paint_draw_cursor, NULL); + } } diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 6e43c0ab492..bc6e019142a 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -55,26 +55,26 @@ bool paint_curve_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - Paint *p; - RegionView3D *rv3d = CTX_wm_region_view3d(C); - SpaceImage *sima; + Object *ob = CTX_data_active_object(C); + Paint *p; + RegionView3D *rv3d = CTX_wm_region_view3d(C); + SpaceImage *sima; - if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0))) - return false; + if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0))) + return false; - sima = CTX_wm_space_image(C); + sima = CTX_wm_space_image(C); - if (sima && sima->mode != SI_MODE_PAINT) - return false; + if (sima && sima->mode != SI_MODE_PAINT) + return false; - p = BKE_paint_get_active_from_context(C); + p = BKE_paint_get_active_from_context(C); - if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { - return true; - } + if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + return true; + } - return false; + return false; } #define SEL_F1 (1 << 0) @@ -82,655 +82,670 @@ bool paint_curve_poll(bContext *C) #define SEL_F3 (1 << 2) /* returns 0, 1, or 2 in point according to handle 1, pivot or handle 2 */ -static PaintCurvePoint *paintcurve_point_get_closest(PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point) +static PaintCurvePoint *paintcurve_point_get_closest( + PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point) { - PaintCurvePoint *pcp, *closest = NULL; - int i; - float dist, closest_dist = FLT_MAX; - - for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { - dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]); - if (dist < threshold) { - if (dist < closest_dist) { - closest = pcp; - closest_dist = dist; - if (point) - *point = SEL_F1; - } - } - if (!ignore_pivot) { - dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]); - if (dist < threshold) { - if (dist < closest_dist) { - closest = pcp; - closest_dist = dist; - if (point) - *point = SEL_F2; - } - } - } - dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]); - if (dist < threshold) { - if (dist < closest_dist) { - closest = pcp; - closest_dist = dist; - if (point) - *point = SEL_F3; - } - } - } - - return closest; + PaintCurvePoint *pcp, *closest = NULL; + int i; + float dist, closest_dist = FLT_MAX; + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F1; + } + } + if (!ignore_pivot) { + dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F2; + } + } + } + dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F3; + } + } + } + + return closest; } static int paintcurve_point_co_index(char sel) { - char i = 0; - while (sel != 1) { - sel >>= 1; - i++; - } - return i; + char i = 0; + while (sel != 1) { + sel >>= 1; + i++; + } + return i; } -static char paintcurve_point_side_index(const BezTriple *bezt, const bool is_first, const char fallback) +static char paintcurve_point_side_index(const BezTriple *bezt, + const bool is_first, + const char fallback) { - /* when matching, guess based on endpoint side */ - if (BEZT_ISSEL_ANY(bezt)) { - if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) { - return is_first ? SEL_F1 : SEL_F3; - } - else if (bezt->f1 & SELECT) { - return SEL_F1; - } - else if (bezt->f3 & SELECT) { - return SEL_F3; - } - else { - return fallback; - } - } - else { - return 0; - } + /* when matching, guess based on endpoint side */ + if (BEZT_ISSEL_ANY(bezt)) { + if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) { + return is_first ? SEL_F1 : SEL_F3; + } + else if (bezt->f1 & SELECT) { + return SEL_F1; + } + else if (bezt->f3 & SELECT) { + return SEL_F3; + } + else { + return fallback; + } + } + else { + return 0; + } } /******************* Operators *********************************/ static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op)) { - Paint *p = BKE_paint_get_active_from_context(C); - Main *bmain = CTX_data_main(C); + Paint *p = BKE_paint_get_active_from_context(C); + Main *bmain = CTX_data_main(C); - if (p && p->brush) { - p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve"); - } + if (p && p->brush) { + p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve"); + } - WM_event_add_notifier(C, NC_PAINTCURVE | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_PAINTCURVE | NA_ADDED, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void PAINTCURVE_OT_new(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add New Paint Curve"; - ot->description = "Add new paint curve"; - ot->idname = "PAINTCURVE_OT_new"; + /* identifiers */ + ot->name = "Add New Paint Curve"; + ot->description = "Add new paint curve"; + ot->idname = "PAINTCURVE_OT_new"; - /* api callbacks */ - ot->exec = paintcurve_new_exec; - ot->poll = paint_curve_poll; + /* api callbacks */ + ot->exec = paintcurve_new_exec; + ot->poll = paint_curve_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) +static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) { - Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = p->brush; - Main *bmain = CTX_data_main(C); - PaintCurve *pc; - PaintCurvePoint *pcp; - wmWindow *window = CTX_wm_window(C); - ARegion *ar = CTX_wm_region(C); - float vec[3] = {loc[0], loc[1], 0.0}; - int add_index; - int i; - - pc = br->paint_curve; - if (!pc) { - br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve"); - } - - ED_paintcurve_undo_push_begin(op->type->name); - - pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint"); - add_index = pc->add_index; - - if (pc->points) { - if (add_index > 0) - memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint)); - if (add_index < pc->tot_points) - memcpy(pcp + add_index + 1, pc->points + add_index, (pc->tot_points - add_index) * sizeof(PaintCurvePoint)); - - MEM_freeN(pc->points); - } - pc->points = pcp; - pc->tot_points++; - - /* initialize new point */ - memset(&pcp[add_index], 0, sizeof(PaintCurvePoint)); - copy_v3_v3(pcp[add_index].bez.vec[0], vec); - copy_v3_v3(pcp[add_index].bez.vec[1], vec); - copy_v3_v3(pcp[add_index].bez.vec[2], vec); - - /* last step, clear selection from all bezier handles expect the next */ - for (i = 0; i < pc->tot_points; i++) { - pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0; - } - - BKE_paint_curve_clamp_endpoint_add_index(pc, add_index); - - if (pc->add_index != 0) { - pcp[add_index].bez.f3 = SELECT; - pcp[add_index].bez.h2 = HD_ALIGN; - } - else { - pcp[add_index].bez.f1 = SELECT; - pcp[add_index].bez.h1 = HD_ALIGN; - } - - ED_paintcurve_undo_push_end(); - - WM_paint_cursor_tag_redraw(window, ar); + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + Main *bmain = CTX_data_main(C); + PaintCurve *pc; + PaintCurvePoint *pcp; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + float vec[3] = {loc[0], loc[1], 0.0}; + int add_index; + int i; + + pc = br->paint_curve; + if (!pc) { + br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve"); + } + + ED_paintcurve_undo_push_begin(op->type->name); + + pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint"); + add_index = pc->add_index; + + if (pc->points) { + if (add_index > 0) + memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint)); + if (add_index < pc->tot_points) + memcpy(pcp + add_index + 1, + pc->points + add_index, + (pc->tot_points - add_index) * sizeof(PaintCurvePoint)); + + MEM_freeN(pc->points); + } + pc->points = pcp; + pc->tot_points++; + + /* initialize new point */ + memset(&pcp[add_index], 0, sizeof(PaintCurvePoint)); + copy_v3_v3(pcp[add_index].bez.vec[0], vec); + copy_v3_v3(pcp[add_index].bez.vec[1], vec); + copy_v3_v3(pcp[add_index].bez.vec[2], vec); + + /* last step, clear selection from all bezier handles expect the next */ + for (i = 0; i < pc->tot_points; i++) { + pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0; + } + + BKE_paint_curve_clamp_endpoint_add_index(pc, add_index); + + if (pc->add_index != 0) { + pcp[add_index].bez.f3 = SELECT; + pcp[add_index].bez.h2 = HD_ALIGN; + } + else { + pcp[add_index].bez.f1 = SELECT; + pcp[add_index].bez.h1 = HD_ALIGN; + } + + ED_paintcurve_undo_push_end(); + + WM_paint_cursor_tag_redraw(window, ar); } - static int paintcurve_add_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int loc[2] = {event->mval[0], event->mval[1]}; - paintcurve_point_add(C, op, loc); - RNA_int_set_array(op->ptr, "location", loc); - return OPERATOR_FINISHED; + int loc[2] = {event->mval[0], event->mval[1]}; + paintcurve_point_add(C, op, loc); + RNA_int_set_array(op->ptr, "location", loc); + return OPERATOR_FINISHED; } static int paintcurve_add_point_exec(bContext *C, wmOperator *op) { - int loc[2]; + int loc[2]; - if (RNA_struct_property_is_set(op->ptr, "location")) { - RNA_int_get_array(op->ptr, "location", loc); - paintcurve_point_add(C, op, loc); - return OPERATOR_FINISHED; - } + if (RNA_struct_property_is_set(op->ptr, "location")) { + RNA_int_get_array(op->ptr, "location", loc); + paintcurve_point_add(C, op, loc); + return OPERATOR_FINISHED; + } - return OPERATOR_CANCELLED; + return OPERATOR_CANCELLED; } void PAINTCURVE_OT_add_point(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add New Paint Curve Point"; - ot->description = ot->name; - ot->idname = "PAINTCURVE_OT_add_point"; - - /* api callbacks */ - ot->invoke = paintcurve_add_point_invoke; - ot->exec = paintcurve_add_point_exec; - ot->poll = paint_curve_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - - /* properties */ - RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX, - "Location", "Location of vertex in area space", 0, SHRT_MAX); + /* identifiers */ + ot->name = "Add New Paint Curve Point"; + ot->description = ot->name; + ot->idname = "PAINTCURVE_OT_add_point"; + + /* api callbacks */ + ot->invoke = paintcurve_add_point_invoke; + ot->exec = paintcurve_add_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_int_vector(ot->srna, + "location", + 2, + NULL, + 0, + SHRT_MAX, + "Location", + "Location of vertex in area space", + 0, + SHRT_MAX); } static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) { - Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = p->brush; - PaintCurve *pc; - PaintCurvePoint *pcp; - wmWindow *window = CTX_wm_window(C); - ARegion *ar = CTX_wm_region(C); - int i; - int tot_del = 0; - pc = br->paint_curve; - - if (!pc || pc->tot_points == 0) { - return OPERATOR_CANCELLED; - } - - ED_paintcurve_undo_push_begin(op->type->name); + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + PaintCurve *pc; + PaintCurvePoint *pcp; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + int i; + int tot_del = 0; + pc = br->paint_curve; + + if (!pc || pc->tot_points == 0) { + return OPERATOR_CANCELLED; + } + + ED_paintcurve_undo_push_begin(op->type->name); #define DELETE_TAG 2 - for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { - if (BEZT_ISSEL_ANY(&pcp->bez)) { - pcp->bez.f2 |= DELETE_TAG; - tot_del++; - } - } - - if (tot_del > 0) { - int j = 0; - int new_tot = pc->tot_points - tot_del; - PaintCurvePoint *points_new = NULL; - if (new_tot > 0) - points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint"); - - for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { - if (!(pcp->bez.f2 & DELETE_TAG)) { - points_new[j] = pc->points[i]; - - if ((i + 1) == pc->add_index) { - BKE_paint_curve_clamp_endpoint_add_index(pc, j); - } - j++; - } - else if ((i + 1) == pc->add_index) { - /* prefer previous point */ - pc->add_index = j; - } - } - MEM_freeN(pc->points); - - pc->points = points_new; - pc->tot_points = new_tot; - } + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + if (BEZT_ISSEL_ANY(&pcp->bez)) { + pcp->bez.f2 |= DELETE_TAG; + tot_del++; + } + } + + if (tot_del > 0) { + int j = 0; + int new_tot = pc->tot_points - tot_del; + PaintCurvePoint *points_new = NULL; + if (new_tot > 0) + points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint"); + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + if (!(pcp->bez.f2 & DELETE_TAG)) { + points_new[j] = pc->points[i]; + + if ((i + 1) == pc->add_index) { + BKE_paint_curve_clamp_endpoint_add_index(pc, j); + } + j++; + } + else if ((i + 1) == pc->add_index) { + /* prefer previous point */ + pc->add_index = j; + } + } + MEM_freeN(pc->points); + + pc->points = points_new; + pc->tot_points = new_tot; + } #undef DELETE_TAG - ED_paintcurve_undo_push_end(); + ED_paintcurve_undo_push_end(); - WM_paint_cursor_tag_redraw(window, ar); + WM_paint_cursor_tag_redraw(window, ar); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } - void PAINTCURVE_OT_delete_point(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove Paint Curve Point"; - ot->description = ot->name; - ot->idname = "PAINTCURVE_OT_delete_point"; + /* identifiers */ + ot->name = "Remove Paint Curve Point"; + ot->description = ot->name; + ot->idname = "PAINTCURVE_OT_delete_point"; - /* api callbacks */ - ot->exec = paintcurve_delete_point_exec; - ot->poll = paint_curve_poll; + /* api callbacks */ + ot->exec = paintcurve_delete_point_exec; + ot->poll = paint_curve_poll; - /* flags */ - ot->flag = OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_UNDO; } - -static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend) +static bool paintcurve_point_select( + bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend) { - wmWindow *window = CTX_wm_window(C); - ARegion *ar = CTX_wm_region(C); - Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = p->brush; - PaintCurve *pc; - int i; - const float loc_fl[2] = {UNPACK2(loc)}; - - pc = br->paint_curve; - - if (!pc) - return false; - - ED_paintcurve_undo_push_begin(op->type->name); - - if (toggle) { - PaintCurvePoint *pcp; - char select = 0; - bool selected = false; - - pcp = pc->points; - - for (i = 0; i < pc->tot_points; i++) { - if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) { - selected = true; - break; - } - } - - if (!selected) { - select = SELECT; - } - - for (i = 0; i < pc->tot_points; i++) { - pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select; - } - } - else { - PaintCurvePoint *pcp; - char selflag; - - pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag); - - if (pcp) { - BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); - - if (selflag == SEL_F2) { - if (extend) - pcp->bez.f2 ^= SELECT; - else - pcp->bez.f2 |= SELECT; - } - else if (selflag == SEL_F1) { - if (extend) - pcp->bez.f1 ^= SELECT; - else - pcp->bez.f1 |= SELECT; - } - else if (selflag == SEL_F3) { - if (extend) - pcp->bez.f3 ^= SELECT; - else - pcp->bez.f3 |= SELECT; - } - } - - /* clear selection for unselected points if not extending and if a point has been selected */ - if (!extend && pcp) { - for (i = 0; i < pc->tot_points; i++) { - pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0; - - if ((pc->points + i) == pcp) { - char index = paintcurve_point_co_index(selflag); - PAINT_CURVE_POINT_SELECT(pcp, index); - } - } - } - - if (!pcp) { - ED_paintcurve_undo_push_end(); - return false; - } - } - - ED_paintcurve_undo_push_end(); - - WM_paint_cursor_tag_redraw(window, ar); - - return true; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + PaintCurve *pc; + int i; + const float loc_fl[2] = {UNPACK2(loc)}; + + pc = br->paint_curve; + + if (!pc) + return false; + + ED_paintcurve_undo_push_begin(op->type->name); + + if (toggle) { + PaintCurvePoint *pcp; + char select = 0; + bool selected = false; + + pcp = pc->points; + + for (i = 0; i < pc->tot_points; i++) { + if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) { + selected = true; + break; + } + } + + if (!selected) { + select = SELECT; + } + + for (i = 0; i < pc->tot_points; i++) { + pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select; + } + } + else { + PaintCurvePoint *pcp; + char selflag; + + pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag); + + if (pcp) { + BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); + + if (selflag == SEL_F2) { + if (extend) + pcp->bez.f2 ^= SELECT; + else + pcp->bez.f2 |= SELECT; + } + else if (selflag == SEL_F1) { + if (extend) + pcp->bez.f1 ^= SELECT; + else + pcp->bez.f1 |= SELECT; + } + else if (selflag == SEL_F3) { + if (extend) + pcp->bez.f3 ^= SELECT; + else + pcp->bez.f3 |= SELECT; + } + } + + /* clear selection for unselected points if not extending and if a point has been selected */ + if (!extend && pcp) { + for (i = 0; i < pc->tot_points; i++) { + pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0; + + if ((pc->points + i) == pcp) { + char index = paintcurve_point_co_index(selflag); + PAINT_CURVE_POINT_SELECT(pcp, index); + } + } + } + + if (!pcp) { + ED_paintcurve_undo_push_end(); + return false; + } + } + + ED_paintcurve_undo_push_end(); + + WM_paint_cursor_tag_redraw(window, ar); + + return true; } - static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int loc[2] = {UNPACK2(event->mval)}; - bool toggle = RNA_boolean_get(op->ptr, "toggle"); - bool extend = RNA_boolean_get(op->ptr, "extend"); - if (paintcurve_point_select(C, op, loc, toggle, extend)) { - RNA_int_set_array(op->ptr, "location", loc); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + int loc[2] = {UNPACK2(event->mval)}; + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool extend = RNA_boolean_get(op->ptr, "extend"); + if (paintcurve_point_select(C, op, loc, toggle, extend)) { + RNA_int_set_array(op->ptr, "location", loc); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } static int paintcurve_select_point_exec(bContext *C, wmOperator *op) { - int loc[2]; + int loc[2]; - if (RNA_struct_property_is_set(op->ptr, "location")) { - bool toggle = RNA_boolean_get(op->ptr, "toggle"); - bool extend = RNA_boolean_get(op->ptr, "extend"); - RNA_int_get_array(op->ptr, "location", loc); - if (paintcurve_point_select(C, op, loc, toggle, extend)) - return OPERATOR_FINISHED; - } + if (RNA_struct_property_is_set(op->ptr, "location")) { + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool extend = RNA_boolean_get(op->ptr, "extend"); + RNA_int_get_array(op->ptr, "location", loc); + if (paintcurve_point_select(C, op, loc, toggle, extend)) + return OPERATOR_FINISHED; + } - return OPERATOR_CANCELLED; + return OPERATOR_CANCELLED; } void PAINTCURVE_OT_select(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Select Paint Curve Point"; - ot->description = "Select a paint curve point"; - ot->idname = "PAINTCURVE_OT_select"; - - /* api callbacks */ - ot->invoke = paintcurve_select_point_invoke; - ot->exec = paintcurve_select_point_exec; - ot->poll = paint_curve_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; - - /* properties */ - RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX, - "Location", "Location of vertex in area space", 0, SHRT_MAX); - prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "(De)select all"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Paint Curve Point"; + ot->description = "Select a paint curve point"; + ot->idname = "PAINTCURVE_OT_select"; + + /* api callbacks */ + ot->invoke = paintcurve_select_point_invoke; + ot->exec = paintcurve_select_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_int_vector(ot->srna, + "location", + 2, + NULL, + 0, + SHRT_MAX, + "Location", + "Location of vertex in area space", + 0, + SHRT_MAX); + prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "(De)select all"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } typedef struct PointSlideData { - PaintCurvePoint *pcp; - char select; - int initial_loc[2]; - float point_initial_loc[3][2]; - int event; - bool align; + PaintCurvePoint *pcp; + char select; + int initial_loc[2]; + float point_initial_loc[3][2]; + int event; + bool align; } PointSlideData; static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Paint *p = BKE_paint_get_active_from_context(C); - const float loc_fl[2] = {UNPACK2(event->mval)}; - char select; - int i; - bool do_select = RNA_boolean_get(op->ptr, "select"); - bool align = RNA_boolean_get(op->ptr, "align"); - Brush *br = p->brush; - PaintCurve *pc = br->paint_curve; - PaintCurvePoint *pcp; - - if (!pc) - return OPERATOR_PASS_THROUGH; - - if (do_select) { - pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select); - } - else { - pcp = NULL; - /* just find first selected point */ - for (i = 0; i < pc->tot_points; i++) { - if ((select = paintcurve_point_side_index(&pc->points[i].bez, i == 0, SEL_F3))) { - pcp = &pc->points[i]; - break; - } - } - } - - - if (pcp) { - ARegion *ar = CTX_wm_region(C); - wmWindow *window = CTX_wm_window(C); - PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData"); - copy_v2_v2_int(psd->initial_loc, event->mval); - psd->event = event->type; - psd->pcp = pcp; - psd->select = paintcurve_point_co_index(select); - for (i = 0; i < 3; i++) { - copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]); - } - psd->align = align; - op->customdata = psd; - - /* first, clear all selection from points */ - for (i = 0; i < pc->tot_points; i++) - pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0; - - /* only select the active point */ - PAINT_CURVE_POINT_SELECT(pcp, psd->select); - BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); - - WM_event_add_modal_handler(C, op); - WM_paint_cursor_tag_redraw(window, ar); - return OPERATOR_RUNNING_MODAL; - } - - return OPERATOR_PASS_THROUGH; + Paint *p = BKE_paint_get_active_from_context(C); + const float loc_fl[2] = {UNPACK2(event->mval)}; + char select; + int i; + bool do_select = RNA_boolean_get(op->ptr, "select"); + bool align = RNA_boolean_get(op->ptr, "align"); + Brush *br = p->brush; + PaintCurve *pc = br->paint_curve; + PaintCurvePoint *pcp; + + if (!pc) + return OPERATOR_PASS_THROUGH; + + if (do_select) { + pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select); + } + else { + pcp = NULL; + /* just find first selected point */ + for (i = 0; i < pc->tot_points; i++) { + if ((select = paintcurve_point_side_index(&pc->points[i].bez, i == 0, SEL_F3))) { + pcp = &pc->points[i]; + break; + } + } + } + + if (pcp) { + ARegion *ar = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData"); + copy_v2_v2_int(psd->initial_loc, event->mval); + psd->event = event->type; + psd->pcp = pcp; + psd->select = paintcurve_point_co_index(select); + for (i = 0; i < 3; i++) { + copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]); + } + psd->align = align; + op->customdata = psd; + + /* first, clear all selection from points */ + for (i = 0; i < pc->tot_points; i++) + pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0; + + /* only select the active point */ + PAINT_CURVE_POINT_SELECT(pcp, psd->select); + BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); + + WM_event_add_modal_handler(C, op); + WM_paint_cursor_tag_redraw(window, ar); + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_PASS_THROUGH; } static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { - PointSlideData *psd = op->customdata; - - if (event->type == psd->event && event->val == KM_RELEASE) { - MEM_freeN(psd); - ED_paintcurve_undo_push_begin(op->type->name); - ED_paintcurve_undo_push_end(); - return OPERATOR_FINISHED; - } - - switch (event->type) { - case MOUSEMOVE: - { - ARegion *ar = CTX_wm_region(C); - wmWindow *window = CTX_wm_window(C); - float diff[2] = { - event->mval[0] - psd->initial_loc[0], - event->mval[1] - psd->initial_loc[1]}; - if (psd->select == 1) { - int i; - for (i = 0; i < 3; i++) - add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]); - } - else { - add_v2_v2(diff, psd->point_initial_loc[psd->select]); - copy_v2_v2(psd->pcp->bez.vec[psd->select], diff); - - if (psd->align) { - char opposite = (psd->select == 0) ? 2 : 0; - sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]); - add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff); - } - } - WM_paint_cursor_tag_redraw(window, ar); - break; - } - default: - break; - } - - return OPERATOR_RUNNING_MODAL; + PointSlideData *psd = op->customdata; + + if (event->type == psd->event && event->val == KM_RELEASE) { + MEM_freeN(psd); + ED_paintcurve_undo_push_begin(op->type->name); + ED_paintcurve_undo_push_end(); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case MOUSEMOVE: { + ARegion *ar = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + float diff[2] = {event->mval[0] - psd->initial_loc[0], event->mval[1] - psd->initial_loc[1]}; + if (psd->select == 1) { + int i; + for (i = 0; i < 3; i++) + add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]); + } + else { + add_v2_v2(diff, psd->point_initial_loc[psd->select]); + copy_v2_v2(psd->pcp->bez.vec[psd->select], diff); + + if (psd->align) { + char opposite = (psd->select == 0) ? 2 : 0; + sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]); + add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff); + } + } + WM_paint_cursor_tag_redraw(window, ar); + break; + } + default: + break; + } + + return OPERATOR_RUNNING_MODAL; } - void PAINTCURVE_OT_slide(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Slide Paint Curve Point"; - ot->description = "Select and slide paint curve point"; - ot->idname = "PAINTCURVE_OT_slide"; - - /* api callbacks */ - ot->invoke = paintcurve_slide_invoke; - ot->modal = paintcurve_slide_modal; - ot->poll = paint_curve_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform"); - RNA_def_boolean(ot->srna, "select", true, "Select", "Attempt to select a point handle before transform"); + /* identifiers */ + ot->name = "Slide Paint Curve Point"; + ot->description = "Select and slide paint curve point"; + ot->idname = "PAINTCURVE_OT_slide"; + + /* api callbacks */ + ot->invoke = paintcurve_slide_invoke; + ot->modal = paintcurve_slide_modal; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean( + ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform"); + RNA_def_boolean( + ot->srna, "select", true, "Select", "Attempt to select a point handle before transform"); } static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op)) { - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - const char *name; - - switch (mode) { - case PAINT_MODE_TEXTURE_2D: - case PAINT_MODE_TEXTURE_3D: - name = "PAINT_OT_image_paint"; - break; - case PAINT_MODE_WEIGHT: - name = "PAINT_OT_weight_paint"; - break; - case PAINT_MODE_VERTEX: - name = "PAINT_OT_vertex_paint"; - break; - case PAINT_MODE_SCULPT: - name = "SCULPT_OT_brush_stroke"; - break; - default: - return OPERATOR_PASS_THROUGH; - } - - return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + const char *name; + + switch (mode) { + case PAINT_MODE_TEXTURE_2D: + case PAINT_MODE_TEXTURE_3D: + name = "PAINT_OT_image_paint"; + break; + case PAINT_MODE_WEIGHT: + name = "PAINT_OT_weight_paint"; + break; + case PAINT_MODE_VERTEX: + name = "PAINT_OT_vertex_paint"; + break; + case PAINT_MODE_SCULPT: + name = "SCULPT_OT_brush_stroke"; + break; + default: + return OPERATOR_PASS_THROUGH; + } + + return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL); } void PAINTCURVE_OT_draw(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Draw Curve"; - ot->description = "Draw curve"; - ot->idname = "PAINTCURVE_OT_draw"; + /* identifiers */ + ot->name = "Draw Curve"; + ot->description = "Draw curve"; + ot->idname = "PAINTCURVE_OT_draw"; - /* api callbacks */ - ot->exec = paintcurve_draw_exec; - ot->poll = paint_curve_poll; + /* api callbacks */ + ot->exec = paintcurve_draw_exec; + ot->poll = paint_curve_poll; - /* flags */ - ot->flag = OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_UNDO; } static int paintcurve_cursor_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - - switch (mode) { - case PAINT_MODE_TEXTURE_2D: - { - ARegion *ar = CTX_wm_region(C); - SpaceImage *sima = CTX_wm_space_image(C); - float location[2]; - - if (!sima) - return OPERATOR_CANCELLED; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]); - copy_v2_v2(sima->cursor, location); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); - break; - } - default: - ED_view3d_cursor3d_update(C, event->mval, true, V3D_CURSOR_ORIENT_VIEW); - break; - } - - return OPERATOR_FINISHED; + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + + switch (mode) { + case PAINT_MODE_TEXTURE_2D: { + ARegion *ar = CTX_wm_region(C); + SpaceImage *sima = CTX_wm_space_image(C); + float location[2]; + + if (!sima) + return OPERATOR_CANCELLED; + + UI_view2d_region_to_view( + &ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]); + copy_v2_v2(sima->cursor, location); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); + break; + } + default: + ED_view3d_cursor3d_update(C, event->mval, true, V3D_CURSOR_ORIENT_VIEW); + break; + } + + return OPERATOR_FINISHED; } void PAINTCURVE_OT_cursor(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Place Cursor"; - ot->description = "Place cursor"; - ot->idname = "PAINTCURVE_OT_cursor"; + /* identifiers */ + ot->name = "Place Cursor"; + ot->description = "Place cursor"; + ot->idname = "PAINTCURVE_OT_cursor"; - /* api callbacks */ - ot->invoke = paintcurve_cursor_invoke; - ot->poll = paint_curve_poll; + /* api callbacks */ + ot->invoke = paintcurve_cursor_invoke; + ot->poll = paint_curve_poll; - /* flags */ - ot->flag = 0; + /* flags */ + ot->flag = 0; } diff --git a/source/blender/editors/sculpt_paint/paint_curve_undo.c b/source/blender/editors/sculpt_paint/paint_curve_undo.c index 1eaed8eabb8..c03cb69df88 100644 --- a/source/blender/editors/sculpt_paint/paint_curve_undo.c +++ b/source/blender/editors/sculpt_paint/paint_curve_undo.c @@ -44,30 +44,30 @@ * \{ */ typedef struct UndoCurve { - PaintCurvePoint *points; /* points of curve */ - int tot_points; - int add_index; + PaintCurvePoint *points; /* points of curve */ + int tot_points; + int add_index; } UndoCurve; static void undocurve_from_paintcurve(UndoCurve *uc, const PaintCurve *pc) { - BLI_assert(BLI_array_is_zeroed(uc, 1)); - uc->points = MEM_dupallocN(pc->points); - uc->tot_points = pc->tot_points; - uc->add_index = pc->add_index; + BLI_assert(BLI_array_is_zeroed(uc, 1)); + uc->points = MEM_dupallocN(pc->points); + uc->tot_points = pc->tot_points; + uc->add_index = pc->add_index; } static void undocurve_to_paintcurve(const UndoCurve *uc, PaintCurve *pc) { - MEM_SAFE_FREE(pc->points); - pc->points = MEM_dupallocN(uc->points); - pc->tot_points = uc->tot_points; - pc->add_index = uc->add_index; + MEM_SAFE_FREE(pc->points); + pc->points = MEM_dupallocN(uc->points); + pc->tot_points = uc->tot_points; + pc->add_index = uc->add_index; } static void undocurve_free_data(UndoCurve *uc) { - MEM_SAFE_FREE(uc->points); + MEM_SAFE_FREE(uc->points); } /** \} */ @@ -77,92 +77,96 @@ static void undocurve_free_data(UndoCurve *uc) * \{ */ typedef struct PaintCurveUndoStep { - UndoStep step; - PaintCurve *pc; - UndoCurve data; + UndoStep step; + PaintCurve *pc; + UndoCurve data; } PaintCurveUndoStep; static bool paintcurve_undosys_poll(bContext *C) { - if (C == NULL || !paint_curve_poll(C)) { - return false; - } - Paint *p = BKE_paint_get_active_from_context(C); - return (p->brush && p->brush->paint_curve); + if (C == NULL || !paint_curve_poll(C)) { + return false; + } + Paint *p = BKE_paint_get_active_from_context(C); + return (p->brush && p->brush->paint_curve); } static void paintcurve_undosys_step_encode_init(struct bContext *C, UndoStep *us_p) { - /* XXX, use to set the undo type only. */ - UNUSED_VARS(C, us_p); + /* XXX, use to set the undo type only. */ + UNUSED_VARS(C, us_p); } -static bool paintcurve_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p) +static bool paintcurve_undosys_step_encode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p) { - if (C == NULL || !paint_curve_poll(C)) { - return false; - } - Paint *p = BKE_paint_get_active_from_context(C); - PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : NULL) : NULL; - if (pc == NULL) { - return false; - } - - PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; - BLI_assert(us->step.data_size == 0); - - us->pc = pc; - undocurve_from_paintcurve(&us->data, pc); - - return true; + if (C == NULL || !paint_curve_poll(C)) { + return false; + } + Paint *p = BKE_paint_get_active_from_context(C); + PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : NULL) : NULL; + if (pc == NULL) { + return false; + } + + PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; + BLI_assert(us->step.data_size == 0); + + us->pc = pc; + undocurve_from_paintcurve(&us->data, pc); + + return true; } -static void paintcurve_undosys_step_decode(struct bContext *UNUSED(C), struct Main *UNUSED(bmain), UndoStep *us_p, int UNUSED(dir)) +static void paintcurve_undosys_step_decode(struct bContext *UNUSED(C), + struct Main *UNUSED(bmain), + UndoStep *us_p, + int UNUSED(dir)) { - PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; - undocurve_to_paintcurve(&us->data, us->pc); + PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; + undocurve_to_paintcurve(&us->data, us->pc); } static void paintcurve_undosys_step_free(UndoStep *us_p) { - PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; - undocurve_free_data(&us->data); + PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p; + undocurve_free_data(&us->data); } /* Export for ED_undo_sys. */ void ED_paintcurve_undosys_type(UndoType *ut) { - ut->name = "Paint Curve"; - /* don't poll for now */ - ut->poll = paintcurve_undosys_poll; - ut->step_encode_init = paintcurve_undosys_step_encode_init; - ut->step_encode = paintcurve_undosys_step_encode; - ut->step_decode = paintcurve_undosys_step_decode; - ut->step_free = paintcurve_undosys_step_free; + ut->name = "Paint Curve"; + /* don't poll for now */ + ut->poll = paintcurve_undosys_poll; + ut->step_encode_init = paintcurve_undosys_step_encode_init; + ut->step_encode = paintcurve_undosys_step_encode; + ut->step_decode = paintcurve_undosys_step_decode; + ut->step_free = paintcurve_undosys_step_free; - ut->use_context = false; + ut->use_context = false; - ut->step_size = sizeof(PaintCurveUndoStep); + ut->step_size = sizeof(PaintCurveUndoStep); } /** \} */ - /* -------------------------------------------------------------------- */ /** \name Utilities * \{ */ void ED_paintcurve_undo_push_begin(const char *name) { - UndoStack *ustack = ED_undo_stack_get(); - bContext *C = NULL; /* special case, we never read from this. */ - BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_PAINTCURVE); + UndoStack *ustack = ED_undo_stack_get(); + bContext *C = NULL; /* special case, we never read from this. */ + BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_PAINTCURVE); } void ED_paintcurve_undo_push_end(void) { - UndoStack *ustack = ED_undo_stack_get(); - BKE_undosys_step_push(ustack, NULL, NULL); + UndoStack *ustack = ED_undo_stack_get(); + BKE_undosys_step_push(ustack, NULL, NULL); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index b64c23ad0b6..c1f87f676e2 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -61,273 +61,241 @@ #include <assert.h> /* return true if the element should be hidden/shown */ -static bool is_effected( - PartialVisArea area, - float planes[4][4], - const float co[3], - const float mask) +static bool is_effected(PartialVisArea area, + float planes[4][4], + const float co[3], + const float mask) { - if (area == PARTIALVIS_ALL) - return 1; - else if (area == PARTIALVIS_MASKED) { - return mask > 0.5f; - } - else { - bool inside = isect_point_planes_v3(planes, 4, co); - return ((inside && area == PARTIALVIS_INSIDE) || - (!inside && area == PARTIALVIS_OUTSIDE)); - } + if (area == PARTIALVIS_ALL) + return 1; + else if (area == PARTIALVIS_MASKED) { + return mask > 0.5f; + } + else { + bool inside = isect_point_planes_v3(planes, 4, co); + return ((inside && area == PARTIALVIS_INSIDE) || (!inside && area == PARTIALVIS_OUTSIDE)); + } } -static void partialvis_update_mesh( - Object *ob, - PBVH *pbvh, - PBVHNode *node, - PartialVisAction action, - PartialVisArea area, - float planes[4][4]) +static void partialvis_update_mesh(Object *ob, + PBVH *pbvh, + PBVHNode *node, + PartialVisAction action, + PartialVisArea area, + float planes[4][4]) { - Mesh *me = ob->data; - MVert *mvert; - const float *paint_mask; - const int *vert_indices; - int totvert, i; - bool any_changed = false, any_visible = false; - - BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert); - BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); - paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK); - - sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); - - for (i = 0; i < totvert; i++) { - MVert *v = &mvert[vert_indices[i]]; - float vmask = paint_mask ? paint_mask[vert_indices[i]] : 0; - - /* hide vertex if in the hide volume */ - if (is_effected(area, planes, v->co, vmask)) { - if (action == PARTIALVIS_HIDE) - v->flag |= ME_HIDE; - else - v->flag &= ~ME_HIDE; - any_changed = true; - } - - if (!(v->flag & ME_HIDE)) - any_visible = true; - } - - if (any_changed) { - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_node_fully_hidden_set(node, !any_visible); - } + Mesh *me = ob->data; + MVert *mvert; + const float *paint_mask; + const int *vert_indices; + int totvert, i; + bool any_changed = false, any_visible = false; + + BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert); + BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); + paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK); + + sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); + + for (i = 0; i < totvert; i++) { + MVert *v = &mvert[vert_indices[i]]; + float vmask = paint_mask ? paint_mask[vert_indices[i]] : 0; + + /* hide vertex if in the hide volume */ + if (is_effected(area, planes, v->co, vmask)) { + if (action == PARTIALVIS_HIDE) + v->flag |= ME_HIDE; + else + v->flag &= ~ME_HIDE; + any_changed = true; + } + + if (!(v->flag & ME_HIDE)) + any_visible = true; + } + + if (any_changed) { + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_node_fully_hidden_set(node, !any_visible); + } } /* Hide or show elements in multires grids with a special GridFlags * customdata layer. */ -static void partialvis_update_grids( - Object *ob, - PBVH *pbvh, - PBVHNode *node, - PartialVisAction action, - PartialVisArea area, - float planes[4][4]) +static void partialvis_update_grids(Object *ob, + PBVH *pbvh, + PBVHNode *node, + PartialVisAction action, + PartialVisArea area, + float planes[4][4]) { - CCGElem **grids; - CCGKey key; - BLI_bitmap **grid_hidden; - int *grid_indices, totgrid, i; - bool any_changed = false, any_visible = false; - - - /* get PBVH data */ - BKE_pbvh_node_get_grids( - pbvh, node, - &grid_indices, &totgrid, NULL, NULL, - &grids); - grid_hidden = BKE_pbvh_grid_hidden(pbvh); - BKE_pbvh_get_grid_key(pbvh, &key); - - sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); - - for (i = 0; i < totgrid; i++) { - int any_hidden = 0; - int g = grid_indices[i], x, y; - BLI_bitmap *gh = grid_hidden[g]; - - if (!gh) { - switch (action) { - case PARTIALVIS_HIDE: - /* create grid flags data */ - gh = grid_hidden[g] = BLI_BITMAP_NEW( - key.grid_area, - "partialvis_update_grids"); - break; - case PARTIALVIS_SHOW: - /* entire grid is visible, nothing to show */ - continue; - } - } - else if (action == PARTIALVIS_SHOW && area == PARTIALVIS_ALL) { - /* special case if we're showing all, just free the - * grid */ - MEM_freeN(gh); - grid_hidden[g] = NULL; - any_changed = true; - any_visible = true; - continue; - } - - for (y = 0; y < key.grid_size; y++) { - for (x = 0; x < key.grid_size; x++) { - CCGElem *elem = CCG_grid_elem(&key, grids[g], x, y); - const float *co = CCG_elem_co(&key, elem); - float mask = key.has_mask ? *CCG_elem_mask(&key, elem) : 0.0f; - - /* skip grid element if not in the effected area */ - if (is_effected(area, planes, co, mask)) { - /* set or clear the hide flag */ - BLI_BITMAP_SET( - gh, y * key.grid_size + x, - action == PARTIALVIS_HIDE); - - any_changed = true; - } - - /* keep track of whether any elements are still hidden */ - if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) - any_hidden = true; - else - any_visible = true; - } - } - - /* if everything in the grid is now visible, free the grid - * flags */ - if (!any_hidden) { - MEM_freeN(gh); - grid_hidden[g] = NULL; - } - } - - /* mark updates if anything was hidden/shown */ - if (any_changed) { - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_node_fully_hidden_set(node, !any_visible); - multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED); - } + CCGElem **grids; + CCGKey key; + BLI_bitmap **grid_hidden; + int *grid_indices, totgrid, i; + bool any_changed = false, any_visible = false; + + /* get PBVH data */ + BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids); + grid_hidden = BKE_pbvh_grid_hidden(pbvh); + BKE_pbvh_get_grid_key(pbvh, &key); + + sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); + + for (i = 0; i < totgrid; i++) { + int any_hidden = 0; + int g = grid_indices[i], x, y; + BLI_bitmap *gh = grid_hidden[g]; + + if (!gh) { + switch (action) { + case PARTIALVIS_HIDE: + /* create grid flags data */ + gh = grid_hidden[g] = BLI_BITMAP_NEW(key.grid_area, "partialvis_update_grids"); + break; + case PARTIALVIS_SHOW: + /* entire grid is visible, nothing to show */ + continue; + } + } + else if (action == PARTIALVIS_SHOW && area == PARTIALVIS_ALL) { + /* special case if we're showing all, just free the + * grid */ + MEM_freeN(gh); + grid_hidden[g] = NULL; + any_changed = true; + any_visible = true; + continue; + } + + for (y = 0; y < key.grid_size; y++) { + for (x = 0; x < key.grid_size; x++) { + CCGElem *elem = CCG_grid_elem(&key, grids[g], x, y); + const float *co = CCG_elem_co(&key, elem); + float mask = key.has_mask ? *CCG_elem_mask(&key, elem) : 0.0f; + + /* skip grid element if not in the effected area */ + if (is_effected(area, planes, co, mask)) { + /* set or clear the hide flag */ + BLI_BITMAP_SET(gh, y * key.grid_size + x, action == PARTIALVIS_HIDE); + + any_changed = true; + } + + /* keep track of whether any elements are still hidden */ + if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) + any_hidden = true; + else + any_visible = true; + } + } + + /* if everything in the grid is now visible, free the grid + * flags */ + if (!any_hidden) { + MEM_freeN(gh); + grid_hidden[g] = NULL; + } + } + + /* mark updates if anything was hidden/shown */ + if (any_changed) { + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_node_fully_hidden_set(node, !any_visible); + multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED); + } } -static void partialvis_update_bmesh_verts( - BMesh *bm, - GSet *verts, - PartialVisAction action, - PartialVisArea area, - float planes[4][4], - bool *any_changed, - bool *any_visible) +static void partialvis_update_bmesh_verts(BMesh *bm, + GSet *verts, + PartialVisAction action, + PartialVisArea area, + float planes[4][4], + bool *any_changed, + bool *any_visible) { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, verts) { - BMVert *v = BLI_gsetIterator_getKey(&gs_iter); - float *vmask = CustomData_bmesh_get( - &bm->vdata, v->head.data, CD_PAINT_MASK); - - /* hide vertex if in the hide volume */ - if (is_effected(area, planes, v->co, *vmask)) { - if (action == PARTIALVIS_HIDE) - BM_elem_flag_enable(v, BM_ELEM_HIDDEN); - else - BM_elem_flag_disable(v, BM_ELEM_HIDDEN); - (*any_changed) = true; - } - - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) - (*any_visible) = true; - } + GSetIterator gs_iter; + + GSET_ITER (gs_iter, verts) { + BMVert *v = BLI_gsetIterator_getKey(&gs_iter); + float *vmask = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK); + + /* hide vertex if in the hide volume */ + if (is_effected(area, planes, v->co, *vmask)) { + if (action == PARTIALVIS_HIDE) + BM_elem_flag_enable(v, BM_ELEM_HIDDEN); + else + BM_elem_flag_disable(v, BM_ELEM_HIDDEN); + (*any_changed) = true; + } + + if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + (*any_visible) = true; + } } static void partialvis_update_bmesh_faces(GSet *faces) { - GSetIterator gs_iter; + GSetIterator gs_iter; - GSET_ITER (gs_iter, faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + GSET_ITER (gs_iter, faces) { + BMFace *f = BLI_gsetIterator_getKey(&gs_iter); - if (paint_is_bmesh_face_hidden(f)) - BM_elem_flag_enable(f, BM_ELEM_HIDDEN); - else - BM_elem_flag_disable(f, BM_ELEM_HIDDEN); - } + if (paint_is_bmesh_face_hidden(f)) + BM_elem_flag_enable(f, BM_ELEM_HIDDEN); + else + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + } } -static void partialvis_update_bmesh( - Object *ob, - PBVH *pbvh, - PBVHNode *node, - PartialVisAction action, - PartialVisArea area, - float planes[4][4]) +static void partialvis_update_bmesh(Object *ob, + PBVH *pbvh, + PBVHNode *node, + PartialVisAction action, + PartialVisArea area, + float planes[4][4]) { - BMesh *bm; - GSet *unique, *other, *faces; - bool any_changed = false, any_visible = false; - - bm = BKE_pbvh_get_bmesh(pbvh); - unique = BKE_pbvh_bmesh_node_unique_verts(node); - other = BKE_pbvh_bmesh_node_other_verts(node); - faces = BKE_pbvh_bmesh_node_faces(node); - - sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); - - partialvis_update_bmesh_verts( - bm, - unique, - action, - area, - planes, - &any_changed, - &any_visible); - - partialvis_update_bmesh_verts( - bm, - other, - action, - area, - planes, - &any_changed, - &any_visible); - - /* finally loop over node faces and tag the ones that are fully hidden */ - partialvis_update_bmesh_faces(faces); - - if (any_changed) { - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_node_fully_hidden_set(node, !any_visible); - } + BMesh *bm; + GSet *unique, *other, *faces; + bool any_changed = false, any_visible = false; + + bm = BKE_pbvh_get_bmesh(pbvh); + unique = BKE_pbvh_bmesh_node_unique_verts(node); + other = BKE_pbvh_bmesh_node_other_verts(node); + faces = BKE_pbvh_bmesh_node_faces(node); + + sculpt_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); + + partialvis_update_bmesh_verts(bm, unique, action, area, planes, &any_changed, &any_visible); + + partialvis_update_bmesh_verts(bm, other, action, area, planes, &any_changed, &any_visible); + + /* finally loop over node faces and tag the ones that are fully hidden */ + partialvis_update_bmesh_faces(faces); + + if (any_changed) { + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_node_fully_hidden_set(node, !any_visible); + } } static void rect_from_props(rcti *rect, PointerRNA *ptr) { - rect->xmin = RNA_int_get(ptr, "xmin"); - rect->ymin = RNA_int_get(ptr, "ymin"); - rect->xmax = RNA_int_get(ptr, "xmax"); - rect->ymax = RNA_int_get(ptr, "ymax"); + rect->xmin = RNA_int_get(ptr, "xmin"); + rect->ymin = RNA_int_get(ptr, "ymin"); + rect->xmax = RNA_int_get(ptr, "xmax"); + rect->ymax = RNA_int_get(ptr, "ymax"); } -static void clip_planes_from_rect( - bContext *C, - float clip_planes[4][4], - const rcti *rect) +static void clip_planes_from_rect(bContext *C, float clip_planes[4][4], const rcti *rect) { - ViewContext vc; - BoundBox bb; + ViewContext vc; + BoundBox bb; - view3d_operator_needs_opengl(C); - ED_view3d_viewcontext_init(C, &vc); - ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, rect); - negate_m4(clip_planes); + view3d_operator_needs_opengl(C); + ED_view3d_viewcontext_init(C, &vc); + ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, rect); + negate_m4(clip_planes); } /* If mode is inside, get all PBVH nodes that lie at least partially @@ -335,144 +303,148 @@ static void clip_planes_from_rect( * that lie at least partially outside the volume. If showing all, get * all nodes. */ static void get_pbvh_nodes( - PBVH *pbvh, - PBVHNode ***nodes, - int *totnode, - float clip_planes[4][4], - PartialVisArea mode) + PBVH *pbvh, PBVHNode ***nodes, int *totnode, float clip_planes[4][4], PartialVisArea mode) { - BKE_pbvh_SearchCallback cb = NULL; - - /* select search callback */ - switch (mode) { - case PARTIALVIS_INSIDE: - cb = BKE_pbvh_node_planes_contain_AABB; - break; - case PARTIALVIS_OUTSIDE: - cb = BKE_pbvh_node_planes_exclude_AABB; - break; - case PARTIALVIS_ALL: - case PARTIALVIS_MASKED: - break; - } - - BKE_pbvh_search_gather(pbvh, cb, clip_planes, nodes, totnode); + BKE_pbvh_SearchCallback cb = NULL; + + /* select search callback */ + switch (mode) { + case PARTIALVIS_INSIDE: + cb = BKE_pbvh_node_planes_contain_AABB; + break; + case PARTIALVIS_OUTSIDE: + cb = BKE_pbvh_node_planes_exclude_AABB; + break; + case PARTIALVIS_ALL: + case PARTIALVIS_MASKED: + break; + } + + BKE_pbvh_search_gather(pbvh, cb, clip_planes, nodes, totnode); } static int hide_show_exec(bContext *C, wmOperator *op) { - ARegion *ar = CTX_wm_region(C); - Object *ob = CTX_data_active_object(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Mesh *me = ob->data; - PartialVisAction action; - PartialVisArea area; - PBVH *pbvh; - PBVHNode **nodes; - PBVHType pbvh_type; - float clip_planes[4][4]; - rcti rect; - int totnode, i; - - /* read operator properties */ - action = RNA_enum_get(op->ptr, "action"); - area = RNA_enum_get(op->ptr, "area"); - rect_from_props(&rect, op->ptr); - - clip_planes_from_rect(C, clip_planes, &rect); - - pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); - BLI_assert(ob->sculpt->pbvh == pbvh); - - get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area); - pbvh_type = BKE_pbvh_type(pbvh); - - /* start undo */ - switch (action) { - case PARTIALVIS_HIDE: - sculpt_undo_push_begin("Hide area"); - break; - case PARTIALVIS_SHOW: - sculpt_undo_push_begin("Show area"); - break; - } - - for (i = 0; i < totnode; i++) { - switch (pbvh_type) { - case PBVH_FACES: - partialvis_update_mesh(ob, pbvh, nodes[i], action, area, clip_planes); - break; - case PBVH_GRIDS: - partialvis_update_grids(ob, pbvh, nodes[i], action, area, clip_planes); - break; - case PBVH_BMESH: - partialvis_update_bmesh(ob, pbvh, nodes[i], action, area, clip_planes); - break; - } - } - - if (nodes) - MEM_freeN(nodes); - - /* end undo */ - sculpt_undo_push_end(); - - /* ensure that edges and faces get hidden as well (not used by - * sculpt but it looks wrong when entering editmode otherwise) */ - if (pbvh_type == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(me); - } - - ED_region_tag_redraw(ar); - - return OPERATOR_FINISHED; + ARegion *ar = CTX_wm_region(C); + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Mesh *me = ob->data; + PartialVisAction action; + PartialVisArea area; + PBVH *pbvh; + PBVHNode **nodes; + PBVHType pbvh_type; + float clip_planes[4][4]; + rcti rect; + int totnode, i; + + /* read operator properties */ + action = RNA_enum_get(op->ptr, "action"); + area = RNA_enum_get(op->ptr, "area"); + rect_from_props(&rect, op->ptr); + + clip_planes_from_rect(C, clip_planes, &rect); + + pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); + BLI_assert(ob->sculpt->pbvh == pbvh); + + get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area); + pbvh_type = BKE_pbvh_type(pbvh); + + /* start undo */ + switch (action) { + case PARTIALVIS_HIDE: + sculpt_undo_push_begin("Hide area"); + break; + case PARTIALVIS_SHOW: + sculpt_undo_push_begin("Show area"); + break; + } + + for (i = 0; i < totnode; i++) { + switch (pbvh_type) { + case PBVH_FACES: + partialvis_update_mesh(ob, pbvh, nodes[i], action, area, clip_planes); + break; + case PBVH_GRIDS: + partialvis_update_grids(ob, pbvh, nodes[i], action, area, clip_planes); + break; + case PBVH_BMESH: + partialvis_update_bmesh(ob, pbvh, nodes[i], action, area, clip_planes); + break; + } + } + + if (nodes) + MEM_freeN(nodes); + + /* end undo */ + sculpt_undo_push_end(); + + /* ensure that edges and faces get hidden as well (not used by + * sculpt but it looks wrong when entering editmode otherwise) */ + if (pbvh_type == PBVH_FACES) { + BKE_mesh_flush_hidden_from_verts(me); + } + + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; } static int hide_show_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - PartialVisArea area = RNA_enum_get(op->ptr, "area"); + PartialVisArea area = RNA_enum_get(op->ptr, "area"); - if (!ELEM(area, PARTIALVIS_ALL, PARTIALVIS_MASKED)) - return WM_gesture_box_invoke(C, op, event); - else - return op->type->exec(C, op); + if (!ELEM(area, PARTIALVIS_ALL, PARTIALVIS_MASKED)) + return WM_gesture_box_invoke(C, op, event); + else + return op->type->exec(C, op); } void PAINT_OT_hide_show(struct wmOperatorType *ot) { - static const EnumPropertyItem action_items[] = { - {PARTIALVIS_HIDE, "HIDE", 0, "Hide", "Hide vertices"}, - {PARTIALVIS_SHOW, "SHOW", 0, "Show", "Show vertices"}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem area_items[] = { - {PARTIALVIS_OUTSIDE, "OUTSIDE", 0, "Outside", "Hide or show vertices outside the selection"}, - {PARTIALVIS_INSIDE, "INSIDE", 0, "Inside", "Hide or show vertices inside the selection"}, - {PARTIALVIS_ALL, "ALL", 0, "All", "Hide or show all vertices"}, - {PARTIALVIS_MASKED, "MASKED", 0, "Masked", "Hide or show vertices that are masked (minimum mask value of 0.5)"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Hide/Show"; - ot->idname = "PAINT_OT_hide_show"; - ot->description = "Hide/show some vertices"; - - /* api callbacks */ - ot->invoke = hide_show_invoke; - ot->modal = WM_gesture_box_modal; - ot->exec = hide_show_exec; - /* sculpt-only for now */ - ot->poll = sculpt_mode_poll_view3d; - - ot->flag = OPTYPE_REGISTER; - - /* rna */ - RNA_def_enum(ot->srna, "action", action_items, PARTIALVIS_HIDE, - "Action", "Whether to hide or show vertices"); - RNA_def_enum(ot->srna, "area", area_items, PARTIALVIS_INSIDE, - "Area", "Which vertices to hide or show"); - - WM_operator_properties_border(ot); + static const EnumPropertyItem action_items[] = { + {PARTIALVIS_HIDE, "HIDE", 0, "Hide", "Hide vertices"}, + {PARTIALVIS_SHOW, "SHOW", 0, "Show", "Show vertices"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem area_items[] = { + {PARTIALVIS_OUTSIDE, "OUTSIDE", 0, "Outside", "Hide or show vertices outside the selection"}, + {PARTIALVIS_INSIDE, "INSIDE", 0, "Inside", "Hide or show vertices inside the selection"}, + {PARTIALVIS_ALL, "ALL", 0, "All", "Hide or show all vertices"}, + {PARTIALVIS_MASKED, + "MASKED", + 0, + "Masked", + "Hide or show vertices that are masked (minimum mask value of 0.5)"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Hide/Show"; + ot->idname = "PAINT_OT_hide_show"; + ot->description = "Hide/show some vertices"; + + /* api callbacks */ + ot->invoke = hide_show_invoke; + ot->modal = WM_gesture_box_modal; + ot->exec = hide_show_exec; + /* sculpt-only for now */ + ot->poll = sculpt_mode_poll_view3d; + + ot->flag = OPTYPE_REGISTER; + + /* rna */ + RNA_def_enum(ot->srna, + "action", + action_items, + PARTIALVIS_HIDE, + "Action", + "Whether to hide or show vertices"); + RNA_def_enum( + ot->srna, "area", area_items, PARTIALVIS_INSIDE, "Area", "Which vertices to hide or show"); + + WM_operator_properties_border(ot); } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 237e60cd5e0..3a3f4335fd5 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -54,7 +54,6 @@ #include "BKE_paint.h" #include "BKE_undo_system.h" - #include "DEG_depsgraph.h" #include "UI_interface.h" @@ -78,7 +77,6 @@ #include "GPU_immediate.h" #include "GPU_state.h" - #include "IMB_colormanagement.h" #include "paint_intern.h" @@ -90,662 +88,679 @@ static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; ImagePaintPartialRedraw *get_imapaintpartial(void) { - return &imapaintpartial; + return &imapaintpartial; } void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) { - imapaintpartial = *ippr; + imapaintpartial = *ippr; } /* Imagepaint Partial Redraw & Dirty Region */ void ED_imapaint_clear_partial_redraw(void) { - memset(&imapaintpartial, 0, sizeof(imapaintpartial)); + memset(&imapaintpartial, 0, sizeof(imapaintpartial)); } -void imapaint_region_tiles(ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th) +void imapaint_region_tiles( + ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th) { - int srcx = 0, srcy = 0; + int srcx = 0, srcy = 0; - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); - *tw = ((x + w - 1) >> IMAPAINT_TILE_BITS); - *th = ((y + h - 1) >> IMAPAINT_TILE_BITS); - *tx = (x >> IMAPAINT_TILE_BITS); - *ty = (y >> IMAPAINT_TILE_BITS); + *tw = ((x + w - 1) >> IMAPAINT_TILE_BITS); + *th = ((y + h - 1) >> IMAPAINT_TILE_BITS); + *tx = (x >> IMAPAINT_TILE_BITS); + *ty = (y >> IMAPAINT_TILE_BITS); } void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old) { - ImBuf *tmpibuf = NULL; - int tilex, tiley, tilew, tileh, tx, ty; - int srcx = 0, srcy = 0; + ImBuf *tmpibuf = NULL; + int tilex, tiley, tilew, tileh, tx, ty; + int srcx = 0, srcy = 0; - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); - if (w == 0 || h == 0) - return; + if (w == 0 || h == 0) + return; - if (!imapaintpartial.enabled) { - imapaintpartial.x1 = x; - imapaintpartial.y1 = y; - imapaintpartial.x2 = x + w; - imapaintpartial.y2 = y + h; - imapaintpartial.enabled = 1; - } - else { - imapaintpartial.x1 = min_ii(imapaintpartial.x1, x); - imapaintpartial.y1 = min_ii(imapaintpartial.y1, y); - imapaintpartial.x2 = max_ii(imapaintpartial.x2, x + w); - imapaintpartial.y2 = max_ii(imapaintpartial.y2, y + h); - } + if (!imapaintpartial.enabled) { + imapaintpartial.x1 = x; + imapaintpartial.y1 = y; + imapaintpartial.x2 = x + w; + imapaintpartial.y2 = y + h; + imapaintpartial.enabled = 1; + } + else { + imapaintpartial.x1 = min_ii(imapaintpartial.x1, x); + imapaintpartial.y1 = min_ii(imapaintpartial.y1, y); + imapaintpartial.x2 = max_ii(imapaintpartial.x2, x + w); + imapaintpartial.y2 = max_ii(imapaintpartial.y2, y + h); + } - imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); + imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh); - ListBase *undo_tiles = ED_image_undo_get_tiles(); + ListBase *undo_tiles = ED_image_undo_get_tiles(); - for (ty = tiley; ty <= tileh; ty++) - for (tx = tilex; tx <= tilew; tx++) - image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); + for (ty = tiley; ty <= tileh; ty++) + for (tx = tilex; tx <= tilew; tx++) + image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); - ibuf->userflags |= IB_BITMAPDIRTY; + ibuf->userflags |= IB_BITMAPDIRTY; - if (tmpibuf) - IMB_freeImBuf(tmpibuf); + if (tmpibuf) + IMB_freeImBuf(tmpibuf); } void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) { - if (imapaintpartial.x1 != imapaintpartial.x2 && - imapaintpartial.y1 != imapaintpartial.y2) - { - IMB_partial_display_buffer_update_delayed( - ibuf, imapaintpartial.x1, imapaintpartial.y1, - imapaintpartial.x2, imapaintpartial.y2); - } - - if (ibuf->mipmap[0]) - ibuf->userflags |= IB_MIPMAP_INVALID; - - /* todo: should set_tpage create ->rect? */ - if (texpaint || (sima && sima->lock)) { - int w = imapaintpartial.x2 - imapaintpartial.x1; - int h = imapaintpartial.y2 - imapaintpartial.y1; - if (w && h) { - /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); - } - } + if (imapaintpartial.x1 != imapaintpartial.x2 && imapaintpartial.y1 != imapaintpartial.y2) { + IMB_partial_display_buffer_update_delayed( + ibuf, imapaintpartial.x1, imapaintpartial.y1, imapaintpartial.x2, imapaintpartial.y2); + } + + if (ibuf->mipmap[0]) + ibuf->userflags |= IB_MIPMAP_INVALID; + + /* todo: should set_tpage create ->rect? */ + if (texpaint || (sima && sima->lock)) { + int w = imapaintpartial.x2 - imapaintpartial.x1; + int h = imapaintpartial.y2 - imapaintpartial.y1; + if (w && h) { + /* Testing with partial update in uv editor too */ + GPU_paint_update_image( + image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); + } + } } /* paint blur kernels. Projective painting enforces use of a 2x2 kernel due to lagging */ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) { - int i, j; - BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); - float radius; - int side; - eBlurKernelType type = br->blur_mode; - - if (proj) { - radius = 0.5f; - - side = kernel->side = 2; - kernel->side_squared = kernel->side * kernel->side; - kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); - kernel->pixel_len = radius; - } - else { - if (br->blur_kernel_radius <= 0) - br->blur_kernel_radius = 1; - - radius = br->blur_kernel_radius; - - side = kernel->side = radius * 2 + 1; - kernel->side_squared = kernel->side * kernel->side; - kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); - kernel->pixel_len = br->blur_kernel_radius; - } - - switch (type) { - case KERNEL_BOX: - for (i = 0; i < kernel->side_squared; i++) - kernel->wdata[i] = 1.0; - break; - - case KERNEL_GAUSSIAN: - { - /* at 3.0 standard deviations distance, kernel is about zero */ - float standard_dev = radius / 3.0f; - - /* make the necessary adjustment to the value for use in the normal distribution formula */ - standard_dev = -standard_dev * standard_dev * 2; - - for (i = 0; i < side; i++) { - for (j = 0; j < side; j++) { - float idist = radius - i; - float jdist = radius - j; - float value = exp((idist * idist + jdist * jdist) / standard_dev); - - kernel->wdata[i + j * side] = value; - } - } - - break; - } - - default: - printf("unidentified kernel type, aborting\n"); - MEM_freeN(kernel->wdata); - MEM_freeN(kernel); - return NULL; - } - - return kernel; + int i, j; + BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); + float radius; + int side; + eBlurKernelType type = br->blur_mode; + + if (proj) { + radius = 0.5f; + + side = kernel->side = 2; + kernel->side_squared = kernel->side * kernel->side; + kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->pixel_len = radius; + } + else { + if (br->blur_kernel_radius <= 0) + br->blur_kernel_radius = 1; + + radius = br->blur_kernel_radius; + + side = kernel->side = radius * 2 + 1; + kernel->side_squared = kernel->side * kernel->side; + kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->pixel_len = br->blur_kernel_radius; + } + + switch (type) { + case KERNEL_BOX: + for (i = 0; i < kernel->side_squared; i++) + kernel->wdata[i] = 1.0; + break; + + case KERNEL_GAUSSIAN: { + /* at 3.0 standard deviations distance, kernel is about zero */ + float standard_dev = radius / 3.0f; + + /* make the necessary adjustment to the value for use in the normal distribution formula */ + standard_dev = -standard_dev * standard_dev * 2; + + for (i = 0; i < side; i++) { + for (j = 0; j < side; j++) { + float idist = radius - i; + float jdist = radius - j; + float value = exp((idist * idist + jdist * jdist) / standard_dev); + + kernel->wdata[i + j * side] = value; + } + } + + break; + } + + default: + printf("unidentified kernel type, aborting\n"); + MEM_freeN(kernel->wdata); + MEM_freeN(kernel); + return NULL; + } + + return kernel; } void paint_delete_blur_kernel(BlurKernel *kernel) { - if (kernel->wdata) - MEM_freeN(kernel->wdata); + if (kernel->wdata) + MEM_freeN(kernel->wdata); } /************************ image paint poll ************************/ static Brush *image_paint_brush(bContext *C) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; - return BKE_paint_brush(&settings->imapaint.paint); + return BKE_paint_brush(&settings->imapaint.paint); } static bool image_paint_poll_ex(bContext *C, bool check_tool) { - Object *obact; - - if (!image_paint_brush(C)) - return 0; - - obact = CTX_data_active_object(C); - if ((obact && obact->mode & OB_MODE_TEXTURE_PAINT) && CTX_wm_region_view3d(C)) { - if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { - return 1; - } - } - else { - SpaceImage *sima = CTX_wm_space_image(C); - - if (sima) { - ARegion *ar = CTX_wm_region(C); - - if ((sima->mode == SI_MODE_PAINT) && ar->regiontype == RGN_TYPE_WINDOW) { - return 1; - } - } - } - - return 0; + Object *obact; + + if (!image_paint_brush(C)) + return 0; + + obact = CTX_data_active_object(C); + if ((obact && obact->mode & OB_MODE_TEXTURE_PAINT) && CTX_wm_region_view3d(C)) { + if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { + return 1; + } + } + else { + SpaceImage *sima = CTX_wm_space_image(C); + + if (sima) { + ARegion *ar = CTX_wm_region(C); + + if ((sima->mode == SI_MODE_PAINT) && ar->regiontype == RGN_TYPE_WINDOW) { + return 1; + } + } + } + + return 0; } static bool image_paint_poll(bContext *C) { - return image_paint_poll_ex(C, true); + return image_paint_poll_ex(C, true); } static bool image_paint_poll_ignore_tool(bContext *C) { - return image_paint_poll_ex(C, false); + return image_paint_poll_ex(C, false); } static bool image_paint_2d_clone_poll(bContext *C) { - Brush *brush = image_paint_brush(C); + Brush *brush = image_paint_brush(C); - if (!CTX_wm_region_view3d(C) && image_paint_poll(C)) - if (brush && (brush->imagepaint_tool == PAINT_TOOL_CLONE)) - if (brush->clone.image) - return 1; + if (!CTX_wm_region_view3d(C) && image_paint_poll(C)) + if (brush && (brush->imagepaint_tool == PAINT_TOOL_CLONE)) + if (brush->clone.image) + return 1; - return 0; + return 0; } /************************ paint operator ************************/ typedef enum eTexPaintMode { - PAINT_MODE_2D, - PAINT_MODE_3D_PROJECT, + PAINT_MODE_2D, + PAINT_MODE_3D_PROJECT, } eTexPaintMode; typedef struct PaintOperation { - eTexPaintMode mode; + eTexPaintMode mode; - void *custom_paint; + void *custom_paint; - float prevmouse[2]; - float startmouse[2]; - double starttime; + float prevmouse[2]; + float startmouse[2]; + double starttime; - void *cursor; - ViewContext vc; + void *cursor; + ViewContext vc; } PaintOperation; bool paint_use_opacity_masking(Brush *brush) { - return ((brush->flag & BRUSH_AIRBRUSH) || - (brush->flag & BRUSH_DRAG_DOT) || - (brush->flag & BRUSH_ANCHORED) || - (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || - (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) || - (brush->imagepaint_tool == PAINT_TOOL_FILL) || - (brush->flag & BRUSH_USE_GRADIENT) || - (brush->mtex.tex && - !ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D)) ? - false : true); + return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) || + (brush->flag & BRUSH_ANCHORED) || (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || + (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) || + (brush->imagepaint_tool == PAINT_TOOL_FILL) || + (brush->flag & BRUSH_USE_GRADIENT) || + (brush->mtex.tex && !ELEM(brush->mtex.brush_map_mode, + MTEX_MAP_MODE_TILED, + MTEX_MAP_MODE_STENCIL, + MTEX_MAP_MODE_3D)) ? + false : + true); } -void paint_brush_color_get( - struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, - float pressure, float color[3], struct ColorManagedDisplay *display) +void paint_brush_color_get(struct Scene *scene, + struct Brush *br, + bool color_correction, + bool invert, + float distance, + float pressure, + float color[3], + struct ColorManagedDisplay *display) { - if (invert) - copy_v3_v3(color, BKE_brush_secondary_color_get(scene, br)); - else { - if (br->flag & BRUSH_USE_GRADIENT) { - float color_gr[4]; - switch (br->gradient_stroke_mode) { - case BRUSH_GRADIENT_PRESSURE: - BKE_colorband_evaluate(br->gradient, pressure, color_gr); - break; - case BRUSH_GRADIENT_SPACING_REPEAT: - { - float coord = fmod(distance / br->gradient_spacing, 1.0); - BKE_colorband_evaluate(br->gradient, coord, color_gr); - break; - } - case BRUSH_GRADIENT_SPACING_CLAMP: - { - BKE_colorband_evaluate(br->gradient, distance / br->gradient_spacing, color_gr); - break; - } - } - copy_v3_v3(color, color_gr); - } - else - copy_v3_v3(color, BKE_brush_color_get(scene, br)); - } - if (color_correction) - IMB_colormanagement_display_to_scene_linear_v3(color, display); + if (invert) + copy_v3_v3(color, BKE_brush_secondary_color_get(scene, br)); + else { + if (br->flag & BRUSH_USE_GRADIENT) { + float color_gr[4]; + switch (br->gradient_stroke_mode) { + case BRUSH_GRADIENT_PRESSURE: + BKE_colorband_evaluate(br->gradient, pressure, color_gr); + break; + case BRUSH_GRADIENT_SPACING_REPEAT: { + float coord = fmod(distance / br->gradient_spacing, 1.0); + BKE_colorband_evaluate(br->gradient, coord, color_gr); + break; + } + case BRUSH_GRADIENT_SPACING_CLAMP: { + BKE_colorband_evaluate(br->gradient, distance / br->gradient_spacing, color_gr); + break; + } + } + copy_v3_v3(color, color_gr); + } + else + copy_v3_v3(color, BKE_brush_color_get(scene, br)); + } + if (color_correction) + IMB_colormanagement_display_to_scene_linear_v3(color, display); } void paint_brush_init_tex(Brush *brush) { - /* init mtex nodes */ - if (brush) { - MTex *mtex = &brush->mtex; - if (mtex->tex && mtex->tex->nodetree) { - /* has internal flag to detect it only does it once */ - ntreeTexBeginExecTree(mtex->tex->nodetree); - } - mtex = &brush->mask_mtex; - if (mtex->tex && mtex->tex->nodetree) { - ntreeTexBeginExecTree(mtex->tex->nodetree); - } - } + /* init mtex nodes */ + if (brush) { + MTex *mtex = &brush->mtex; + if (mtex->tex && mtex->tex->nodetree) { + /* has internal flag to detect it only does it once */ + ntreeTexBeginExecTree(mtex->tex->nodetree); + } + mtex = &brush->mask_mtex; + if (mtex->tex && mtex->tex->nodetree) { + ntreeTexBeginExecTree(mtex->tex->nodetree); + } + } } void paint_brush_exit_tex(Brush *brush) { - if (brush) { - MTex *mtex = &brush->mtex; - if (mtex->tex && mtex->tex->nodetree) - ntreeTexEndExecTree(mtex->tex->nodetree->execdata); - mtex = &brush->mask_mtex; - if (mtex->tex && mtex->tex->nodetree) - ntreeTexEndExecTree(mtex->tex->nodetree->execdata); - } + if (brush) { + MTex *mtex = &brush->mtex; + if (mtex->tex && mtex->tex->nodetree) + ntreeTexEndExecTree(mtex->tex->nodetree->execdata); + mtex = &brush->mask_mtex; + if (mtex->tex && mtex->tex->nodetree) + ntreeTexEndExecTree(mtex->tex->nodetree->execdata); + } } static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) { - PaintOperation *pop = (PaintOperation *)customdata; + PaintOperation *pop = (PaintOperation *)customdata; - if (pop) { - GPU_line_smooth(true); - GPU_blend(true); + if (pop) { + GPU_line_smooth(true); + GPU_blend(true); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - ARegion *ar = pop->vc.ar; + ARegion *ar = pop->vc.ar; - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_line_width(4.0); - immUniformColor4ub(0, 0, 0, 255); + GPU_line_width(4.0); + immUniformColor4ub(0, 0, 0, 255); - immBegin(GPU_PRIM_LINES, 2); - immVertex2i(pos, x, y); - immVertex2i(pos, pop->startmouse[0] + ar->winrct.xmin, pop->startmouse[1] + ar->winrct.ymin); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex2i(pos, x, y); + immVertex2i(pos, pop->startmouse[0] + ar->winrct.xmin, pop->startmouse[1] + ar->winrct.ymin); + immEnd(); - GPU_line_width(2.0); - immUniformColor4ub(255, 255, 255, 255); + GPU_line_width(2.0); + immUniformColor4ub(255, 255, 255, 255); - immBegin(GPU_PRIM_LINES, 2); - immVertex2i(pos, x, y); - immVertex2i(pos, pop->startmouse[0] + ar->winrct.xmin, pop->startmouse[1] + ar->winrct.ymin); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex2i(pos, x, y); + immVertex2i(pos, pop->startmouse[0] + ar->winrct.xmin, pop->startmouse[1] + ar->winrct.ymin); + immEnd(); - immUnbindProgram(); + immUnbindProgram(); - GPU_blend(false); - GPU_line_smooth(false); - } + GPU_blend(false); + GPU_line_smooth(false); + } } - static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ - Brush *brush = BKE_paint_brush(&settings->imapaint.paint); - int mode = RNA_enum_get(op->ptr, "mode"); - ED_view3d_viewcontext_init(C, &pop->vc); - - copy_v2_v2(pop->prevmouse, mouse); - copy_v2_v2(pop->startmouse, mouse); - - /* initialize from context */ - if (CTX_wm_region_view3d(C)) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - bool uvs, mat, tex, stencil; - if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { - BKE_paint_data_warning(op->reports, uvs, mat, tex, stencil); - MEM_freeN(pop); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - return NULL; - } - pop->mode = PAINT_MODE_3D_PROJECT; - pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); - } - else { - pop->mode = PAINT_MODE_2D; - pop->custom_paint = paint_2d_new_stroke(C, op, mode); - } - - if (!pop->custom_paint) { - MEM_freeN(pop); - return NULL; - } - - if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { - pop->cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - image_paint_poll, gradient_draw_line, - pop); - } - - settings->imapaint.flag |= IMAGEPAINT_DRAWING; - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - - return pop; + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); + int mode = RNA_enum_get(op->ptr, "mode"); + ED_view3d_viewcontext_init(C, &pop->vc); + + copy_v2_v2(pop->prevmouse, mouse); + copy_v2_v2(pop->startmouse, mouse); + + /* initialize from context */ + if (CTX_wm_region_view3d(C)) { + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + bool uvs, mat, tex, stencil; + if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { + BKE_paint_data_warning(op->reports, uvs, mat, tex, stencil); + MEM_freeN(pop); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + return NULL; + } + pop->mode = PAINT_MODE_3D_PROJECT; + pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); + } + else { + pop->mode = PAINT_MODE_2D; + pop->custom_paint = paint_2d_new_stroke(C, op, mode); + } + + if (!pop->custom_paint) { + MEM_freeN(pop); + return NULL; + } + + if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { + pop->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + image_paint_poll, + gradient_draw_line, + pop); + } + + settings->imapaint.flag |= IMAGEPAINT_DRAWING; + ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); + + return pop; } static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { - PaintOperation *pop = paint_stroke_mode_data(stroke); - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = CTX_data_tool_settings(C); - UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; - Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - - float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; - - /* initial brush values. Maybe it should be considered moving these to stroke system */ - float startalpha = BKE_brush_alpha_get(scene, brush); - - float mouse[2]; - float pressure; - float size; - float distance = paint_stroke_distance_get(stroke); - int eraser; - - RNA_float_get_array(itemptr, "mouse", mouse); - pressure = RNA_float_get(itemptr, "pressure"); - eraser = RNA_boolean_get(itemptr, "pen_flip"); - size = max_ff(1.0f, RNA_float_get(itemptr, "size")); - - /* stroking with fill tool only acts on stroke end */ - if (brush->imagepaint_tool == PAINT_TOOL_FILL) { - copy_v2_v2(pop->prevmouse, mouse); - return; - } - - if (BKE_brush_use_alpha_pressure(scene, brush)) - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); - else - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); - - if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { - UndoStack *ustack = CTX_wm_manager(C)->undo_stack; - ED_image_undo_restore(ustack->step_init); - } - - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); - } - else { - paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); - } - - copy_v2_v2(pop->prevmouse, mouse); - - /* restore brush values */ - BKE_brush_alpha_set(scene, brush, startalpha); + PaintOperation *pop = paint_stroke_mode_data(stroke); + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; + + /* initial brush values. Maybe it should be considered moving these to stroke system */ + float startalpha = BKE_brush_alpha_get(scene, brush); + + float mouse[2]; + float pressure; + float size; + float distance = paint_stroke_distance_get(stroke); + int eraser; + + RNA_float_get_array(itemptr, "mouse", mouse); + pressure = RNA_float_get(itemptr, "pressure"); + eraser = RNA_boolean_get(itemptr, "pen_flip"); + size = max_ff(1.0f, RNA_float_get(itemptr, "size")); + + /* stroking with fill tool only acts on stroke end */ + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + copy_v2_v2(pop->prevmouse, mouse); + return; + } + + if (BKE_brush_use_alpha_pressure(scene, brush)) + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); + else + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); + + if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { + UndoStack *ustack = CTX_wm_manager(C)->undo_stack; + ED_image_undo_restore(ustack->step_init); + } + + if (pop->mode == PAINT_MODE_3D_PROJECT) { + paint_proj_stroke( + C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); + } + else { + paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); + } + + copy_v2_v2(pop->prevmouse, mouse); + + /* restore brush values */ + BKE_brush_alpha_set(scene, brush, startalpha); } static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) { - PaintOperation *pop = paint_stroke_mode_data(stroke); - - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_redraw(C, pop->custom_paint, final); - } - else { - paint_2d_redraw(C, pop->custom_paint, final); - } + PaintOperation *pop = paint_stroke_mode_data(stroke); + + if (pop->mode == PAINT_MODE_3D_PROJECT) { + paint_proj_redraw(C, pop->custom_paint, final); + } + else { + paint_2d_redraw(C, pop->custom_paint, final); + } } static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) { - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = scene->toolsettings; - PaintOperation *pop = paint_stroke_mode_data(stroke); - Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - - toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - - if (brush->imagepaint_tool == PAINT_TOOL_FILL) { - if (brush->flag & BRUSH_USE_GRADIENT) { - if (pop->mode == PAINT_MODE_2D) { - paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); - } - else { - paint_proj_stroke( - C, pop->custom_paint, pop->startmouse, pop->prevmouse, paint_stroke_flipped(stroke), - 1.0, 0.0, BKE_brush_size_get(scene, brush)); - /* two redraws, one for GPU update, one for notification */ - paint_proj_redraw(C, pop->custom_paint, false); - paint_proj_redraw(C, pop->custom_paint, true); - } - } - else { - if (pop->mode == PAINT_MODE_2D) { - float color[3]; - if (paint_stroke_inverted(stroke)) { - srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush)); - } - else { - srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); - } - paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); - } - else { - paint_proj_stroke( - C, pop->custom_paint, pop->startmouse, pop->prevmouse, paint_stroke_flipped(stroke), - 1.0, 0.0, BKE_brush_size_get(scene, brush)); - /* two redraws, one for GPU update, one for notification */ - paint_proj_redraw(C, pop->custom_paint, false); - paint_proj_redraw(C, pop->custom_paint, true); - } - } - } - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke_done(pop->custom_paint); - } - else { - paint_2d_stroke_done(pop->custom_paint); - } - - if (pop->cursor) { - WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor); - } - - ED_image_undo_push_end(); - - /* duplicate warning, see texpaint_init */ + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = scene->toolsettings; + PaintOperation *pop = paint_stroke_mode_data(stroke); + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + if (pop->mode == PAINT_MODE_2D) { + paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, + pop->custom_paint, + pop->startmouse, + pop->prevmouse, + paint_stroke_flipped(stroke), + 1.0, + 0.0, + BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + else { + if (pop->mode == PAINT_MODE_2D) { + float color[3]; + if (paint_stroke_inverted(stroke)) { + srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush)); + } + else { + srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); + } + paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, + pop->custom_paint, + pop->startmouse, + pop->prevmouse, + paint_stroke_flipped(stroke), + 1.0, + 0.0, + BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + } + if (pop->mode == PAINT_MODE_3D_PROJECT) { + paint_proj_stroke_done(pop->custom_paint); + } + else { + paint_2d_stroke_done(pop->custom_paint); + } + + if (pop->cursor) { + WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor); + } + + ED_image_undo_push_end(); + + /* duplicate warning, see texpaint_init */ #if 0 - if (pop->s.warnmultifile) - BKE_reportf(op->reports, RPT_WARNING, "Image requires 4 color channels to paint: %s", pop->s.warnmultifile); - if (pop->s.warnpackedfile) - BKE_reportf(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted: %s", pop->s.warnpackedfile); + if (pop->s.warnmultifile) + BKE_reportf(op->reports, RPT_WARNING, "Image requires 4 color channels to paint: %s", pop->s.warnmultifile); + if (pop->s.warnpackedfile) + BKE_reportf(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted: %s", pop->s.warnpackedfile); #endif - MEM_freeN(pop); + MEM_freeN(pop); } static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { - PaintOperation *pop; + PaintOperation *pop; - /* TODO Should avoid putting this here. Instead, last position should be requested - * from stroke system. */ + /* TODO Should avoid putting this here. Instead, last position should be requested + * from stroke system. */ - if (!(pop = texture_paint_init(C, op, mouse))) { - return false; - } + if (!(pop = texture_paint_init(C, op, mouse))) { + return false; + } - paint_stroke_set_mode_data(op->customdata, pop); + paint_stroke_set_mode_data(op->customdata, pop); - return true; + return true; } - static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int retval; - - op->customdata = paint_stroke_new( - C, op, NULL, paint_stroke_test_start, - paint_stroke_update_step, - paint_stroke_redraw, - paint_stroke_done, event->type); - - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); - - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); - - return OPERATOR_RUNNING_MODAL; + int retval; + + op->customdata = paint_stroke_new(C, + op, + NULL, + paint_stroke_test_start, + paint_stroke_update_step, + paint_stroke_redraw, + paint_stroke_done, + event->type); + + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); + + return OPERATOR_RUNNING_MODAL; } static int paint_exec(bContext *C, wmOperator *op) { - PropertyRNA *strokeprop; - PointerRNA firstpoint; - float mouse[2]; - - strokeprop = RNA_struct_find_property(op->ptr, "stroke"); - - if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) - return OPERATOR_CANCELLED; - - RNA_float_get_array(&firstpoint, "mouse", mouse); - - op->customdata = paint_stroke_new( - C, op, NULL, paint_stroke_test_start, - paint_stroke_update_step, - paint_stroke_redraw, - paint_stroke_done, 0); - /* frees op->customdata */ - return paint_stroke_exec(C, op); + PropertyRNA *strokeprop; + PointerRNA firstpoint; + float mouse[2]; + + strokeprop = RNA_struct_find_property(op->ptr, "stroke"); + + if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) + return OPERATOR_CANCELLED; + + RNA_float_get_array(&firstpoint, "mouse", mouse); + + op->customdata = paint_stroke_new(C, + op, + NULL, + paint_stroke_test_start, + paint_stroke_update_step, + paint_stroke_redraw, + paint_stroke_done, + 0); + /* frees op->customdata */ + return paint_stroke_exec(C, op); } void PAINT_OT_image_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Image Paint"; - ot->idname = "PAINT_OT_image_paint"; - ot->description = "Paint a stroke into the image"; - - /* api callbacks */ - ot->invoke = paint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = paint_exec; - ot->poll = image_paint_poll; - ot->cancel = paint_stroke_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; - - paint_stroke_operator_properties(ot); + /* identifiers */ + ot->name = "Image Paint"; + ot->idname = "PAINT_OT_image_paint"; + ot->description = "Paint a stroke into the image"; + + /* api callbacks */ + ot->invoke = paint_invoke; + ot->modal = paint_stroke_modal; + ot->exec = paint_exec; + ot->poll = image_paint_poll; + ot->cancel = paint_stroke_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; + + paint_stroke_operator_properties(ot); } - int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); - if (!rv3d) { - SpaceImage *sima = CTX_wm_space_image(C); + if (!rv3d) { + SpaceImage *sima = CTX_wm_space_image(C); - if (sima->mode == SI_MODE_PAINT) { - ARegion *ar = CTX_wm_region(C); - ED_space_image_get_zoom(sima, ar, zoomx, zoomy); + if (sima->mode == SI_MODE_PAINT) { + ARegion *ar = CTX_wm_region(C); + ED_space_image_get_zoom(sima, ar, zoomx, zoomy); - return 1; - } - } + return 1; + } + } - *zoomx = *zoomy = 1; + *zoomx = *zoomy = 1; - return 0; + return 0; } /************************ cursor drawing *******************************/ static void toggle_paint_cursor(bContext *C, int enable) { - wmWindowManager *wm = CTX_wm_manager(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - - if (settings->imapaint.paintcursor && !enable) { - WM_paint_cursor_end(wm, settings->imapaint.paintcursor); - settings->imapaint.paintcursor = NULL; - paint_cursor_delete_textures(); - } - else if (enable) - paint_cursor_start(C, image_paint_poll); + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + + if (settings->imapaint.paintcursor && !enable) { + WM_paint_cursor_end(wm, settings->imapaint.paintcursor); + settings->imapaint.paintcursor = NULL; + paint_cursor_delete_textures(); + } + else if (enable) + paint_cursor_start(C, image_paint_poll); } /* enable the paint cursor if it isn't already. @@ -755,526 +770,534 @@ static void toggle_paint_cursor(bContext *C, int enable) * ensure that the cursor is hidden when not in paint mode */ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene) { - ToolSettings *settings = scene->toolsettings; - ImagePaintSettings *imapaint = &settings->imapaint; - bool enabled = false; - - for (wmWindow *win = wm->windows.first; win; win = win->next) { - bScreen *screen = WM_window_get_active_screen(win); - - for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { - if (sa->spacetype == SPACE_IMAGE) { - if (((SpaceImage *)sa->spacedata.first)->mode == SI_MODE_PAINT) { - enabled = true; - } - } - } - } - - if (enabled) { - BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_2D, PAINT_CURSOR_TEXTURE_PAINT); - - paint_cursor_start_explicit(&imapaint->paint, wm, image_paint_poll); - } - else { - paint_cursor_delete_textures(); - } + ToolSettings *settings = scene->toolsettings; + ImagePaintSettings *imapaint = &settings->imapaint; + bool enabled = false; + + for (wmWindow *win = wm->windows.first; win; win = win->next) { + bScreen *screen = WM_window_get_active_screen(win); + + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + if (sa->spacetype == SPACE_IMAGE) { + if (((SpaceImage *)sa->spacedata.first)->mode == SI_MODE_PAINT) { + enabled = true; + } + } + } + } + + if (enabled) { + BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_2D, PAINT_CURSOR_TEXTURE_PAINT); + + paint_cursor_start_explicit(&imapaint->paint, wm, image_paint_poll); + } + else { + paint_cursor_delete_textures(); + } } /************************ grab clone operator ************************/ typedef struct GrabClone { - float startoffset[2]; - int startx, starty; + float startoffset[2]; + int startx, starty; } GrabClone; static void grab_clone_apply(bContext *C, wmOperator *op) { - Brush *brush = image_paint_brush(C); - float delta[2]; + Brush *brush = image_paint_brush(C); + float delta[2]; - RNA_float_get_array(op->ptr, "delta", delta); - add_v2_v2(brush->clone.offset, delta); - ED_region_tag_redraw(CTX_wm_region(C)); + RNA_float_get_array(op->ptr, "delta", delta); + add_v2_v2(brush->clone.offset, delta); + ED_region_tag_redraw(CTX_wm_region(C)); } static int grab_clone_exec(bContext *C, wmOperator *op) { - grab_clone_apply(C, op); + grab_clone_apply(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Brush *brush = image_paint_brush(C); - GrabClone *cmv; + Brush *brush = image_paint_brush(C); + GrabClone *cmv; - cmv = MEM_callocN(sizeof(GrabClone), "GrabClone"); - copy_v2_v2(cmv->startoffset, brush->clone.offset); - cmv->startx = event->x; - cmv->starty = event->y; - op->customdata = cmv; + cmv = MEM_callocN(sizeof(GrabClone), "GrabClone"); + copy_v2_v2(cmv->startoffset, brush->clone.offset); + cmv->startx = event->x; + cmv->starty = event->y; + op->customdata = cmv; - WM_event_add_modal_handler(C, op); + WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Brush *brush = image_paint_brush(C); - ARegion *ar = CTX_wm_region(C); - GrabClone *cmv = op->customdata; - float startfx, startfy, fx, fy, delta[2]; - int xmin = ar->winrct.xmin, ymin = ar->winrct.ymin; - - switch (event->type) { - case LEFTMOUSE: - case MIDDLEMOUSE: - case RIGHTMOUSE: // XXX hardcoded - MEM_freeN(op->customdata); - return OPERATOR_FINISHED; - case MOUSEMOVE: - /* mouse moved, so move the clone image */ - UI_view2d_region_to_view(&ar->v2d, cmv->startx - xmin, cmv->starty - ymin, &startfx, &startfy); - UI_view2d_region_to_view(&ar->v2d, event->x - xmin, event->y - ymin, &fx, &fy); - - delta[0] = fx - startfx; - delta[1] = fy - startfy; - RNA_float_set_array(op->ptr, "delta", delta); - - copy_v2_v2(brush->clone.offset, cmv->startoffset); - - grab_clone_apply(C, op); - break; - } - - return OPERATOR_RUNNING_MODAL; + Brush *brush = image_paint_brush(C); + ARegion *ar = CTX_wm_region(C); + GrabClone *cmv = op->customdata; + float startfx, startfy, fx, fy, delta[2]; + int xmin = ar->winrct.xmin, ymin = ar->winrct.ymin; + + switch (event->type) { + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: // XXX hardcoded + MEM_freeN(op->customdata); + return OPERATOR_FINISHED; + case MOUSEMOVE: + /* mouse moved, so move the clone image */ + UI_view2d_region_to_view( + &ar->v2d, cmv->startx - xmin, cmv->starty - ymin, &startfx, &startfy); + UI_view2d_region_to_view(&ar->v2d, event->x - xmin, event->y - ymin, &fx, &fy); + + delta[0] = fx - startfx; + delta[1] = fy - startfy; + RNA_float_set_array(op->ptr, "delta", delta); + + copy_v2_v2(brush->clone.offset, cmv->startoffset); + + grab_clone_apply(C, op); + break; + } + + return OPERATOR_RUNNING_MODAL; } static void grab_clone_cancel(bContext *UNUSED(C), wmOperator *op) { - MEM_freeN(op->customdata); + MEM_freeN(op->customdata); } void PAINT_OT_grab_clone(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Grab Clone"; - ot->idname = "PAINT_OT_grab_clone"; - ot->description = "Move the clone source image"; - - /* api callbacks */ - ot->exec = grab_clone_exec; - ot->invoke = grab_clone_invoke; - ot->modal = grab_clone_modal; - ot->cancel = grab_clone_cancel; - ot->poll = image_paint_2d_clone_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - RNA_def_float_vector(ot->srna, "delta", 2, NULL, -FLT_MAX, FLT_MAX, "Delta", "Delta offset of clone image in 0.0..1.0 coordinates", -1.0f, 1.0f); + /* identifiers */ + ot->name = "Grab Clone"; + ot->idname = "PAINT_OT_grab_clone"; + ot->description = "Move the clone source image"; + + /* api callbacks */ + ot->exec = grab_clone_exec; + ot->invoke = grab_clone_invoke; + ot->modal = grab_clone_modal; + ot->cancel = grab_clone_cancel; + ot->poll = image_paint_2d_clone_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + RNA_def_float_vector(ot->srna, + "delta", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Delta", + "Delta offset of clone image in 0.0..1.0 coordinates", + -1.0f, + 1.0f); } /******************** sample color operator ********************/ typedef struct { - bool show_cursor; - short event_type; - float initcolor[3]; - bool sample_palette; -} SampleColorData; - + bool show_cursor; + short event_type; + float initcolor[3]; + bool sample_palette; +} SampleColorData; static void sample_color_update_header(SampleColorData *data, bContext *C) { - char msg[UI_MAX_DRAW_STR]; - ScrArea *sa = CTX_wm_area(C); - - if (sa) { - BLI_snprintf( - msg, sizeof(msg), - IFACE_("Sample color for %s"), - !data->sample_palette ? - IFACE_("Brush. Use Left Click to sample for palette instead") : - IFACE_("Palette. Use Left Click to sample more colors")); - ED_workspace_status_text(C, msg); - } + char msg[UI_MAX_DRAW_STR]; + ScrArea *sa = CTX_wm_area(C); + + if (sa) { + BLI_snprintf(msg, + sizeof(msg), + IFACE_("Sample color for %s"), + !data->sample_palette ? + IFACE_("Brush. Use Left Click to sample for palette instead") : + IFACE_("Palette. Use Left Click to sample more colors")); + ED_workspace_status_text(C, msg); + } } static int sample_color_exec(bContext *C, wmOperator *op) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - ARegion *ar = CTX_wm_region(C); - wmWindow *win = CTX_wm_window(C); - const bool show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); - int location[2]; - paint->flags &= ~PAINT_SHOW_BRUSH; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + ARegion *ar = CTX_wm_region(C); + wmWindow *win = CTX_wm_window(C); + const bool show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); + int location[2]; + paint->flags &= ~PAINT_SHOW_BRUSH; - /* force redraw without cursor */ - WM_paint_cursor_tag_redraw(win, ar); - WM_redraw_windows(C); + /* force redraw without cursor */ + WM_paint_cursor_tag_redraw(win, ar); + WM_redraw_windows(C); - RNA_int_get_array(op->ptr, "location", location); - const bool use_palette = RNA_boolean_get(op->ptr, "palette"); - const bool use_sample_texture = (mode == PAINT_MODE_TEXTURE_3D) && !RNA_boolean_get(op->ptr, "merged"); + RNA_int_get_array(op->ptr, "location", location); + const bool use_palette = RNA_boolean_get(op->ptr, "palette"); + const bool use_sample_texture = (mode == PAINT_MODE_TEXTURE_3D) && + !RNA_boolean_get(op->ptr, "merged"); - paint_sample_color(C, ar, location[0], location[1], use_sample_texture, use_palette); + paint_sample_color(C, ar, location[0], location[1], use_sample_texture, use_palette); - if (show_cursor) { - paint->flags |= PAINT_SHOW_BRUSH; - } + if (show_cursor) { + paint->flags |= PAINT_SHOW_BRUSH; + } - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); - ARegion *ar = CTX_wm_region(C); - wmWindow *win = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); + ARegion *ar = CTX_wm_region(C); + wmWindow *win = CTX_wm_window(C); - data->event_type = event->type; - data->show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); - copy_v3_v3(data->initcolor, BKE_brush_color_get(scene, brush)); - data->sample_palette = false; - op->customdata = data; - paint->flags &= ~PAINT_SHOW_BRUSH; + data->event_type = event->type; + data->show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); + copy_v3_v3(data->initcolor, BKE_brush_color_get(scene, brush)); + data->sample_palette = false; + op->customdata = data; + paint->flags &= ~PAINT_SHOW_BRUSH; - sample_color_update_header(data, C); + sample_color_update_header(data, C); - WM_event_add_modal_handler(C, op); + WM_event_add_modal_handler(C, op); - /* force redraw without cursor */ - WM_paint_cursor_tag_redraw(win, ar); - WM_redraw_windows(C); + /* force redraw without cursor */ + WM_paint_cursor_tag_redraw(win, ar); + WM_redraw_windows(C); - RNA_int_set_array(op->ptr, "location", event->mval); + RNA_int_set_array(op->ptr, "location", event->mval); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - const bool use_sample_texture = (mode == PAINT_MODE_TEXTURE_3D) && !RNA_boolean_get(op->ptr, "merged"); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + const bool use_sample_texture = (mode == PAINT_MODE_TEXTURE_3D) && + !RNA_boolean_get(op->ptr, "merged"); - paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, false); - WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); + paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, false); + WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Scene *scene = CTX_data_scene(C); - SampleColorData *data = op->customdata; - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - - if ((event->type == data->event_type) && (event->val == KM_RELEASE)) { - if (data->show_cursor) { - paint->flags |= PAINT_SHOW_BRUSH; - } - - if (data->sample_palette) { - BKE_brush_color_set(scene, brush, data->initcolor); - RNA_boolean_set(op->ptr, "palette", true); - } - WM_cursor_modal_restore(CTX_wm_window(C)); - MEM_freeN(data); - ED_workspace_status_text(C, NULL); - - return OPERATOR_FINISHED; - } - - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - const bool use_sample_texture = (mode == PAINT_MODE_TEXTURE_3D) && !RNA_boolean_get(op->ptr, "merged"); - - switch (event->type) { - case MOUSEMOVE: - { - ARegion *ar = CTX_wm_region(C); - RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, false); - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - break; - } - - case LEFTMOUSE: - if (event->val == KM_PRESS) { - ARegion *ar = CTX_wm_region(C); - RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, true); - if (!data->sample_palette) { - data->sample_palette = true; - sample_color_update_header(data, C); - } - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - } - break; - } - - return OPERATOR_RUNNING_MODAL; + Scene *scene = CTX_data_scene(C); + SampleColorData *data = op->customdata; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + + if ((event->type == data->event_type) && (event->val == KM_RELEASE)) { + if (data->show_cursor) { + paint->flags |= PAINT_SHOW_BRUSH; + } + + if (data->sample_palette) { + BKE_brush_color_set(scene, brush, data->initcolor); + RNA_boolean_set(op->ptr, "palette", true); + } + WM_cursor_modal_restore(CTX_wm_window(C)); + MEM_freeN(data); + ED_workspace_status_text(C, NULL); + + return OPERATOR_FINISHED; + } + + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + const bool use_sample_texture = (mode == PAINT_MODE_TEXTURE_3D) && + !RNA_boolean_get(op->ptr, "merged"); + + switch (event->type) { + case MOUSEMOVE: { + ARegion *ar = CTX_wm_region(C); + RNA_int_set_array(op->ptr, "location", event->mval); + paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, false); + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + break; + } + + case LEFTMOUSE: + if (event->val == KM_PRESS) { + ARegion *ar = CTX_wm_region(C); + RNA_int_set_array(op->ptr, "location", event->mval); + paint_sample_color(C, ar, event->mval[0], event->mval[1], use_sample_texture, true); + if (!data->sample_palette) { + data->sample_palette = true; + sample_color_update_header(data, C); + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + } + break; + } + + return OPERATOR_RUNNING_MODAL; } static bool sample_color_poll(bContext *C) { - return (image_paint_poll_ignore_tool(C) || vertex_paint_poll_ignore_tool(C)); + return (image_paint_poll_ignore_tool(C) || vertex_paint_poll_ignore_tool(C)); } void PAINT_OT_sample_color(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sample Color"; - ot->idname = "PAINT_OT_sample_color"; - ot->description = "Use the mouse to sample a color in the image"; + /* identifiers */ + ot->name = "Sample Color"; + ot->idname = "PAINT_OT_sample_color"; + ot->description = "Use the mouse to sample a color in the image"; - /* api callbacks */ - ot->exec = sample_color_exec; - ot->invoke = sample_color_invoke; - ot->modal = sample_color_modal; - ot->poll = sample_color_poll; + /* api callbacks */ + ot->exec = sample_color_exec; + ot->invoke = sample_color_invoke; + ot->modal = sample_color_modal; + ot->poll = sample_color_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* properties */ - PropertyRNA *prop; + /* properties */ + PropertyRNA *prop; - prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); - RNA_def_boolean(ot->srna, "merged", 0, "Sample Merged", "Sample the output display color"); - RNA_def_boolean(ot->srna, "palette", 0, "Add to Palette", ""); + RNA_def_boolean(ot->srna, "merged", 0, "Sample Merged", "Sample the output display color"); + RNA_def_boolean(ot->srna, "palette", 0, "Add to Palette", ""); } /******************** texture paint toggle operator ********************/ static bool texture_paint_toggle_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->type != OB_MESH) - return 0; - if (!ob->data || ID_IS_LINKED(ob->data)) - return 0; - if (CTX_data_edit_object(C)) - return 0; - - return 1; + Object *ob = CTX_data_active_object(C); + if (ob == NULL || ob->type != OB_MESH) + return 0; + if (!ob->data || ID_IS_LINKED(ob->data)) + return 0; + if (CTX_data_edit_object(C)) + return 0; + + return 1; } static int texture_paint_toggle_exec(bContext *C, wmOperator *op) { - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - const int mode_flag = OB_MODE_TEXTURE_PAINT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; - - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } - - if (ob->mode & mode_flag) { - ob->mode &= ~mode_flag; - - if (U.glreslimit != 0) - GPU_free_images(bmain); - GPU_paint_set_mipmap(bmain, 1); - - toggle_paint_cursor(C, 0); - } - else { - bScreen *sc; - Image *ima = NULL; - ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; - - /* This has to stay here to regenerate the texture paint - * cache in case we are loading a file */ - BKE_texpaint_slots_refresh_object(scene, ob); - - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - - /* entering paint mode also sets image to editors */ - if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { - /* set the current material active paint slot on image editor */ - Material *ma = give_current_material(ob, ob->actcol); - - if (ma && ma->texpaintslot) - ima = ma->texpaintslot[ma->paint_active_slot].ima; - } - else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { - ima = imapaint->canvas; - } - - if (ima) { - for (sc = bmain->screens.first; sc; sc = sc->id.next) { - ScrArea *sa; - for (sa = sc->areabase.first; sa; sa = sa->next) { - SpaceLink *sl; - for (sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_IMAGE) { - SpaceImage *sima = (SpaceImage *)sl; - - if (!sima->pin) { - Object *obedit = CTX_data_edit_object(C); - ED_space_image_set(bmain, sima, obedit, ima, true); - } - } - } - } - } - } - - ob->mode |= mode_flag; - - BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_3D, PAINT_CURSOR_TEXTURE_PAINT); - - BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint); - - if (U.glreslimit != 0) - GPU_free_images(bmain); - GPU_paint_set_mipmap(bmain, 0); - - toggle_paint_cursor(C, 1); - } - - Mesh *me = BKE_mesh_from_object(ob); - BLI_assert(me != NULL); - DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); - - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - - WM_toolsystem_update_from_context_view3d(C); - - return OPERATOR_FINISHED; + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + const int mode_flag = OB_MODE_TEXTURE_PAINT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + if (ob->mode & mode_flag) { + ob->mode &= ~mode_flag; + + if (U.glreslimit != 0) + GPU_free_images(bmain); + GPU_paint_set_mipmap(bmain, 1); + + toggle_paint_cursor(C, 0); + } + else { + bScreen *sc; + Image *ima = NULL; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + + /* This has to stay here to regenerate the texture paint + * cache in case we are loading a file */ + BKE_texpaint_slots_refresh_object(scene, ob); + + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + + /* entering paint mode also sets image to editors */ + if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + /* set the current material active paint slot on image editor */ + Material *ma = give_current_material(ob, ob->actcol); + + if (ma && ma->texpaintslot) + ima = ma->texpaintslot[ma->paint_active_slot].ima; + } + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { + ima = imapaint->canvas; + } + + if (ima) { + for (sc = bmain->screens.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + + if (!sima->pin) { + Object *obedit = CTX_data_edit_object(C); + ED_space_image_set(bmain, sima, obedit, ima, true); + } + } + } + } + } + } + + ob->mode |= mode_flag; + + BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_3D, PAINT_CURSOR_TEXTURE_PAINT); + + BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint); + + if (U.glreslimit != 0) + GPU_free_images(bmain); + GPU_paint_set_mipmap(bmain, 0); + + toggle_paint_cursor(C, 1); + } + + Mesh *me = BKE_mesh_from_object(ob); + BLI_assert(me != NULL); + DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + + WM_toolsystem_update_from_context_view3d(C); + + return OPERATOR_FINISHED; } void PAINT_OT_texture_paint_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Texture Paint Toggle"; - ot->idname = "PAINT_OT_texture_paint_toggle"; - ot->description = "Toggle texture paint mode in 3D view"; + /* identifiers */ + ot->name = "Texture Paint Toggle"; + ot->idname = "PAINT_OT_texture_paint_toggle"; + ot->description = "Toggle texture paint mode in 3D view"; - /* api callbacks */ - ot->exec = texture_paint_toggle_exec; - ot->poll = texture_paint_toggle_poll; + /* api callbacks */ + ot->exec = texture_paint_toggle_exec; + ot->poll = texture_paint_toggle_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } - static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - - ViewLayer *view_layer = CTX_data_view_layer(C); - Paint *paint = BKE_paint_get_active(scene, view_layer); - Brush *br = BKE_paint_brush(paint); - - if (ups->flag & UNIFIED_PAINT_COLOR) { - swap_v3_v3(ups->rgb, ups->secondary_rgb); - } - else if (br) { - swap_v3_v3(br->rgb, br->secondary_rgb); - } - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, br); - - return OPERATOR_FINISHED; + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + ViewLayer *view_layer = CTX_data_view_layer(C); + Paint *paint = BKE_paint_get_active(scene, view_layer); + Brush *br = BKE_paint_brush(paint); + + if (ups->flag & UNIFIED_PAINT_COLOR) { + swap_v3_v3(ups->rgb, ups->secondary_rgb); + } + else if (br) { + swap_v3_v3(br->rgb, br->secondary_rgb); + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, br); + + return OPERATOR_FINISHED; } static bool brush_colors_flip_poll(bContext *C) { - if (image_paint_poll(C)) { - Brush *br = image_paint_brush(C); - if (ELEM(br->imagepaint_tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) - return true; - } - else { - Object *ob = CTX_data_active_object(C); - if (ob != NULL) { - if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) { - return true; - } - } - } - return false; + if (image_paint_poll(C)) { + Brush *br = image_paint_brush(C); + if (ELEM(br->imagepaint_tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) + return true; + } + else { + Object *ob = CTX_data_active_object(C); + if (ob != NULL) { + if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT)) { + return true; + } + } + } + return false; } void PAINT_OT_brush_colors_flip(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Brush Colors Flip"; - ot->idname = "PAINT_OT_brush_colors_flip"; - ot->description = "Toggle foreground and background brush colors"; + /* identifiers */ + ot->name = "Brush Colors Flip"; + ot->idname = "PAINT_OT_brush_colors_flip"; + ot->description = "Toggle foreground and background brush colors"; - /* api callbacks */ - ot->exec = brush_colors_flip_exec; - ot->poll = brush_colors_flip_poll; + /* api callbacks */ + ot->exec = brush_colors_flip_exec; + ot->poll = brush_colors_flip_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) { - wmWindowManager *wm = CTX_wm_manager(C); - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = sima->image; + wmWindowManager *wm = CTX_wm_manager(C); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; - BKE_undosys_step_push_init_with_type(wm->undo_stack, C, op->type->name, BKE_UNDOSYS_TYPE_IMAGE); + BKE_undosys_step_push_init_with_type(wm->undo_stack, C, op->type->name, BKE_UNDOSYS_TYPE_IMAGE); - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); + ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - paint_2d_bucket_fill(C, color, NULL, NULL, NULL); + paint_2d_bucket_fill(C, color, NULL, NULL, NULL); - BKE_undosys_step_push(wm->undo_stack, C, op->type->name); + BKE_undosys_step_push(wm->undo_stack, C, op->type->name); - DEG_id_tag_update(&ima->id, 0); + DEG_id_tag_update(&ima->id, 0); } - static bool texture_paint_poll(bContext *C) { - if (texture_paint_toggle_poll(C)) - if (CTX_data_active_object(C)->mode & OB_MODE_TEXTURE_PAINT) - return 1; + if (texture_paint_toggle_poll(C)) + if (CTX_data_active_object(C)->mode & OB_MODE_TEXTURE_PAINT) + return 1; - return 0; + return 0; } bool image_texture_paint_poll(bContext *C) { - return (texture_paint_poll(C) || image_paint_poll(C)); + return (texture_paint_poll(C) || image_paint_poll(C)); } bool facemask_paint_poll(bContext *C) { - return BKE_paint_select_face_test(CTX_data_active_object(C)); + return BKE_paint_select_face_test(CTX_data_active_object(C)); } bool vert_paint_poll(bContext *C) { - return BKE_paint_select_vert_test(CTX_data_active_object(C)); + return BKE_paint_select_vert_test(CTX_data_active_object(C)); } bool mask_paint_poll(bContext *C) { - return BKE_paint_select_elem_test(CTX_data_active_object(C)); + return BKE_paint_select_elem_test(CTX_data_active_object(C)); } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index cad75efbf56..5390f18304a 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -30,7 +30,6 @@ #include "DNA_space_types.h" #include "DNA_object_types.h" - #include "BLI_math_color_blend.h" #include "BLI_stack.h" #include "BLI_bitmap.h" @@ -57,7 +56,6 @@ #include "UI_view2d.h" - #include "GPU_draw.h" #include "paint_intern.h" @@ -67,1677 +65,1854 @@ /* Defines and Structs */ typedef struct BrushPainterCache { - bool use_float; /* need float imbuf? */ - bool use_color_correction; /* use color correction for float */ - bool invert; - - bool is_texbrush; - bool is_maskbrush; - - int lastdiameter; - float last_tex_rotation; - float last_mask_rotation; - float last_pressure; - - ImBuf *ibuf; - ImBuf *texibuf; - unsigned short *curve_mask; - unsigned short *tex_mask; - unsigned short *tex_mask_old; - unsigned int tex_mask_old_w; - unsigned int tex_mask_old_h; + bool use_float; /* need float imbuf? */ + bool use_color_correction; /* use color correction for float */ + bool invert; + + bool is_texbrush; + bool is_maskbrush; + + int lastdiameter; + float last_tex_rotation; + float last_mask_rotation; + float last_pressure; + + ImBuf *ibuf; + ImBuf *texibuf; + unsigned short *curve_mask; + unsigned short *tex_mask; + unsigned short *tex_mask_old; + unsigned int tex_mask_old_w; + unsigned int tex_mask_old_h; } BrushPainterCache; typedef struct BrushPainter { - Scene *scene; - Brush *brush; + Scene *scene; + Brush *brush; - float lastpaintpos[2]; /* position of last paint op */ - float startpaintpos[2]; /* position of first paint */ + float lastpaintpos[2]; /* position of last paint op */ + float startpaintpos[2]; /* position of first paint */ - short firsttouch; /* first paint op */ + short firsttouch; /* first paint op */ - struct ImagePool *pool; /* image pool */ - rctf tex_mapping; /* texture coordinate mapping */ - rctf mask_mapping; /* mask texture coordinate mapping */ + struct ImagePool *pool; /* image pool */ + rctf tex_mapping; /* texture coordinate mapping */ + rctf mask_mapping; /* mask texture coordinate mapping */ - BrushPainterCache cache; + BrushPainterCache cache; } BrushPainter; typedef struct ImagePaintRegion { - int destx, desty; - int srcx, srcy; - int width, height; + int destx, desty; + int srcx, srcy; + int width, height; } ImagePaintRegion; typedef struct ImagePaintState { - BrushPainter *painter; - SpaceImage *sima; - View2D *v2d; - Scene *scene; - bScreen *screen; - struct ImagePool *image_pool; - - Brush *brush; - short tool, blend; - Image *image; - ImBuf *canvas; - ImBuf *clonecanvas; - const char *warnpackedfile; - const char *warnmultifile; - - bool do_masking; - - /* viewport texture paint only, but _not_ project paint */ - Object *ob; - int faceindex; - float uv[2]; - int do_facesel; - int symmetry; - - bool need_redraw; - - BlurKernel *blurkernel; + BrushPainter *painter; + SpaceImage *sima; + View2D *v2d; + Scene *scene; + bScreen *screen; + struct ImagePool *image_pool; + + Brush *brush; + short tool, blend; + Image *image; + ImBuf *canvas; + ImBuf *clonecanvas; + const char *warnpackedfile; + const char *warnmultifile; + + bool do_masking; + + /* viewport texture paint only, but _not_ project paint */ + Object *ob; + int faceindex; + float uv[2]; + int do_facesel; + int symmetry; + + bool need_redraw; + + BlurKernel *blurkernel; } ImagePaintState; - static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool invert) { - BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); + BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); - painter->brush = brush; - painter->scene = scene; - painter->firsttouch = 1; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - painter->cache.invert = invert; + painter->brush = brush; + painter->scene = scene; + painter->firsttouch = 1; + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ + painter->cache.invert = invert; - return painter; + return painter; } - -static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction) +static void brush_painter_2d_require_imbuf(BrushPainter *painter, + bool use_float, + bool use_color_correction) { - Brush *brush = painter->brush; - - if ((painter->cache.use_float != use_float)) { - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); - if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); - if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); - painter->cache.ibuf = NULL; - painter->cache.curve_mask = NULL; - painter->cache.tex_mask = NULL; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - } - - painter->cache.use_float = use_float; - painter->cache.use_color_correction = use_float && use_color_correction; - painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; + Brush *brush = painter->brush; + + if ((painter->cache.use_float != use_float)) { + if (painter->cache.ibuf) + IMB_freeImBuf(painter->cache.ibuf); + if (painter->cache.curve_mask) + MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) + MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) + MEM_freeN(painter->cache.tex_mask_old); + painter->cache.ibuf = NULL; + painter->cache.curve_mask = NULL; + painter->cache.tex_mask = NULL; + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ + } + + painter->cache.use_float = use_float; + painter->cache.use_color_correction = use_float && use_color_correction; + painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? + true : + false; + painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; } static void brush_painter_2d_free(BrushPainter *painter) { - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); - if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); - if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); - MEM_freeN(painter); + if (painter->cache.ibuf) + IMB_freeImBuf(painter->cache.ibuf); + if (painter->cache.texibuf) + IMB_freeImBuf(painter->cache.texibuf); + if (painter->cache.curve_mask) + MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) + MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) + MEM_freeN(painter->cache.tex_mask_old); + MEM_freeN(painter); } static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) { - texco[0] = mapping->xmin + x * mapping->xmax; - texco[1] = mapping->ymin + y * mapping->ymax; - texco[2] = 0.0f; + texco[0] = mapping->xmin + x * mapping->xmax; + texco[1] = mapping->ymin + y * mapping->ymax; + texco[2] = 0.0f; } /* create a mask with the mask texture */ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - rctf mask_mapping = painter->mask_mapping; - struct ImagePool *pool = painter->pool; - - float texco[3]; - unsigned short *mask, *m; - int x, y, thread = 0; - - mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); - m = mask; - - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++, m++) { - float res; - brush_imbuf_tex_co(&mask_mapping, x, y, texco); - res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); - *m = (unsigned short)(65535.0f * res); - } - } - - return mask; + Scene *scene = painter->scene; + Brush *brush = painter->brush; + rctf mask_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + + float texco[3]; + unsigned short *mask, *m; + int x, y, thread = 0; + + mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); + m = mask; + + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++, m++) { + float res; + brush_imbuf_tex_co(&mask_mapping, x, y, texco); + res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); + *m = (unsigned short)(65535.0f * res); + } + } + + return mask; } /* update rectangular section of the brush image */ -static void brush_painter_mask_imbuf_update( - BrushPainter *painter, unsigned short *tex_mask_old, - int origx, int origy, int w, int h, int xt, int yt, int diameter) +static void brush_painter_mask_imbuf_update(BrushPainter *painter, + unsigned short *tex_mask_old, + int origx, + int origy, + int w, + int h, + int xt, + int yt, + int diameter) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - rctf tex_mapping = painter->mask_mapping; - struct ImagePool *pool = painter->pool; - unsigned short res; - - bool use_texture_old = (tex_mask_old != NULL); - - int x, y, thread = 0; - - unsigned short *tex_mask = painter->cache.tex_mask; - unsigned short *tex_mask_cur = painter->cache.tex_mask_old; - - /* fill pixels */ - for (y = origy; y < h; y++) { - for (x = origx; x < w; x++) { - /* sample texture */ - float texco[3]; - - /* handle byte pixel */ - unsigned short *b = tex_mask + (y * diameter + x); - unsigned short *t = tex_mask_cur + (y * diameter + x); - - if (!use_texture_old) { - brush_imbuf_tex_co(&tex_mapping, x, y, texco); - res = (unsigned short)(65535.0f * BKE_brush_sample_masktex(scene, brush, texco, thread, pool)); - } - - /* read from old texture buffer */ - if (use_texture_old) { - res = *(tex_mask_old + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); - } - - /* write to new texture mask */ - *t = res; - /* write to mask image buffer */ - *b = res; - } - } + Scene *scene = painter->scene; + Brush *brush = painter->brush; + rctf tex_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + unsigned short res; + + bool use_texture_old = (tex_mask_old != NULL); + + int x, y, thread = 0; + + unsigned short *tex_mask = painter->cache.tex_mask; + unsigned short *tex_mask_cur = painter->cache.tex_mask_old; + + /* fill pixels */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture */ + float texco[3]; + + /* handle byte pixel */ + unsigned short *b = tex_mask + (y * diameter + x); + unsigned short *t = tex_mask_cur + (y * diameter + x); + + if (!use_texture_old) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + res = (unsigned short)(65535.0f * + BKE_brush_sample_masktex(scene, brush, texco, thread, pool)); + } + + /* read from old texture buffer */ + if (use_texture_old) { + res = *(tex_mask_old + + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); + } + + /* write to new texture mask */ + *t = res; + /* write to mask image buffer */ + *b = res; + } + } } - /** * Update the brush mask image by trying to reuse the cached texture result. * This can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, + const float pos[2], + int diameter) { - BrushPainterCache *cache = &painter->cache; - unsigned short *tex_mask_old; - int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - - /* create brush image buffer if it didn't exist yet */ - if (!cache->tex_mask) - cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); - - /* create new texture image buffer with coordinates relative to old */ - tex_mask_old = cache->tex_mask_old; - cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); - - if (tex_mask_old) { - ImBuf maskibuf; - ImBuf maskibuf_old; - maskibuf.x = maskibuf.y = diameter; - maskibuf_old.x = cache->tex_mask_old_w; - maskibuf_old.y = cache->tex_mask_old_h; - - srcx = srcy = 0; - w = cache->tex_mask_old_w; - h = cache->tex_mask_old_h; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); - - /* hack, use temporary rects so that clipping works */ - IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); - } - else { - srcx = srcy = 0; - destx = desty = 0; - w = h = 0; - } - - x1 = min_ii(destx, diameter); - y1 = min_ii(desty, diameter); - x2 = min_ii(destx + w, diameter); - y2 = min_ii(desty + h, diameter); - - /* blend existing texture in new position */ - if ((x1 < x2) && (y1 < y2)) - brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); - - if (tex_mask_old) - MEM_freeN(tex_mask_old); - - /* sample texture in new areas */ - if ((0 < x1) && (0 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); - if ((x2 < diameter) && (0 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); - if ((x1 < x2) && (0 < y1)) - brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); - if ((x1 < x2) && (y2 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); - - /* through with sampling, now update sizes */ - cache->tex_mask_old_w = diameter; - cache->tex_mask_old_h = diameter; + BrushPainterCache *cache = &painter->cache; + unsigned short *tex_mask_old; + int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + /* create brush image buffer if it didn't exist yet */ + if (!cache->tex_mask) + cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, + "brush_painter_mask"); + + /* create new texture image buffer with coordinates relative to old */ + tex_mask_old = cache->tex_mask_old; + cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, + "brush_painter_mask"); + + if (tex_mask_old) { + ImBuf maskibuf; + ImBuf maskibuf_old; + maskibuf.x = maskibuf.y = diameter; + maskibuf_old.x = cache->tex_mask_old_w; + maskibuf_old.y = cache->tex_mask_old_h; + + srcx = srcy = 0; + w = cache->tex_mask_old_w; + h = cache->tex_mask_old_h; + destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + + /* hack, use temporary rects so that clipping works */ + IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); + } + else { + srcx = srcy = 0; + destx = desty = 0; + w = h = 0; + } + + x1 = min_ii(destx, diameter); + y1 = min_ii(desty, diameter); + x2 = min_ii(destx + w, diameter); + y2 = min_ii(desty + h, diameter); + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); + + if (tex_mask_old) + MEM_freeN(tex_mask_old); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); + if ((x2 < diameter) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); + if ((x1 < x2) && (0 < y1)) + brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); + if ((x1 < x2) && (y2 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); + + /* through with sampling, now update sizes */ + cache->tex_mask_old_w = diameter; + cache->tex_mask_old_h = diameter; } /* create a mask with the falloff strength */ -static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, float radius) +static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, + int diameter, + float radius) { - Brush *brush = painter->brush; + Brush *brush = painter->brush; - int xoff = -radius; - int yoff = -radius; + int xoff = -radius; + int yoff = -radius; - unsigned short *mask, *m; - int x, y; + unsigned short *mask, *m; + int x, y; - mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); - m = mask; + mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + m = mask; - for (y = 0; y < diameter; y++) { - for (x = 0; x < diameter; x++, m++) { - float xy[2] = {x + xoff, y + yoff}; - float len = len_v2(xy); + for (y = 0; y < diameter; y++) { + for (x = 0; x < diameter; x++, m++) { + float xy[2] = {x + xoff, y + yoff}; + float len = len_v2(xy); - *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); - } - } + *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); + } + } - return mask; + return mask; } - /* create imbuf with brush color */ -static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pressure, float distance) +static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, + int size, + float pressure, + float distance) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - - const char *display_device = scene->display_settings.display_device; - struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); - - rctf tex_mapping = painter->tex_mapping; - struct ImagePool *pool = painter->pool; - - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; - - int x, y, thread = 0; - float brush_rgb[3]; - - /* allocate image buffer */ - ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); - - /* get brush color */ - if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, distance, pressure, brush_rgb, display); - } - else { - brush_rgb[0] = 1.0f; - brush_rgb[1] = 1.0f; - brush_rgb[2] = 1.0f; - } - - /* fill image buffer */ - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++) { - /* sample texture and multiply with brush color */ - float texco[3], rgba[4]; - - if (is_texbrush) { - brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); - /* TODO(sergey): Support texture paint color space. */ - if (!use_float) { - IMB_colormanagement_scene_linear_to_display_v3(rgba, display); - } - mul_v3_v3(rgba, brush_rgb); - } - else { - copy_v3_v3(rgba, brush_rgb); - rgba[3] = 1.0f; - } - - if (use_float) { - /* write to float pixel */ - float *dstf = ibuf->rect_float + (y * size + x) * 4; - mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ - dstf[3] = rgba[3]; - } - else { - /* write to byte pixel */ - unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; - - rgb_float_to_uchar(dst, rgba); - dst[3] = unit_float_to_uchar_clamp(rgba[3]); - } - } - } - - return ibuf; + Scene *scene = painter->scene; + Brush *brush = painter->brush; + + const char *display_device = scene->display_settings.display_device; + struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + + rctf tex_mapping = painter->tex_mapping; + struct ImagePool *pool = painter->pool; + + bool use_color_correction = painter->cache.use_color_correction; + bool use_float = painter->cache.use_float; + bool is_texbrush = painter->cache.is_texbrush; + + int x, y, thread = 0; + float brush_rgb[3]; + + /* allocate image buffer */ + ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); + + /* get brush color */ + if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { + paint_brush_color_get(scene, + brush, + use_color_correction, + painter->cache.invert, + distance, + pressure, + brush_rgb, + display); + } + else { + brush_rgb[0] = 1.0f; + brush_rgb[1] = 1.0f; + brush_rgb[2] = 1.0f; + } + + /* fill image buffer */ + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++) { + /* sample texture and multiply with brush color */ + float texco[3], rgba[4]; + + if (is_texbrush) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + /* TODO(sergey): Support texture paint color space. */ + if (!use_float) { + IMB_colormanagement_scene_linear_to_display_v3(rgba, display); + } + mul_v3_v3(rgba, brush_rgb); + } + else { + copy_v3_v3(rgba, brush_rgb); + rgba[3] = 1.0f; + } + + if (use_float) { + /* write to float pixel */ + float *dstf = ibuf->rect_float + (y * size + x) * 4; + mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ + dstf[3] = rgba[3]; + } + else { + /* write to byte pixel */ + unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; + + rgb_float_to_uchar(dst, rgba); + dst[3] = unit_float_to_uchar_clamp(rgba[3]); + } + } + } + + return ibuf; } /* update rectangular section of the brush image */ static void brush_painter_imbuf_update( - BrushPainter *painter, ImBuf *oldtexibuf, - int origx, int origy, int w, int h, int xt, int yt) + BrushPainter *painter, ImBuf *oldtexibuf, int origx, int origy, int w, int h, int xt, int yt) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - - const char *display_device = scene->display_settings.display_device; - struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); - - rctf tex_mapping = painter->tex_mapping; - struct ImagePool *pool = painter->pool; - - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; - bool use_texture_old = (oldtexibuf != NULL); - - int x, y, thread = 0; - float brush_rgb[3]; - - ImBuf *ibuf = painter->cache.ibuf; - ImBuf *texibuf = painter->cache.texibuf; - - /* get brush color */ - if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); - } - else { - brush_rgb[0] = 1.0f; - brush_rgb[1] = 1.0f; - brush_rgb[2] = 1.0f; - } - - /* fill pixels */ - for (y = origy; y < h; y++) { - for (x = origx; x < w; x++) { - /* sample texture and multiply with brush color */ - float texco[3], rgba[4]; - - if (!use_texture_old) { - if (is_texbrush) { - brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); - /* TODO(sergey): Support texture paint color space. */ - if (!use_float) { - IMB_colormanagement_scene_linear_to_display_v3(rgba, display); - } - mul_v3_v3(rgba, brush_rgb); - } - else { - copy_v3_v3(rgba, brush_rgb); - rgba[3] = 1.0f; - } - } - - if (use_float) { - /* handle float pixel */ - float *bf = ibuf->rect_float + (y * ibuf->x + x) * 4; - float *tf = texibuf->rect_float + (y * texibuf->x + x) * 4; - - /* read from old texture buffer */ - if (use_texture_old) { - const float *otf = oldtexibuf->rect_float + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; - copy_v4_v4(rgba, otf); - } - - /* write to new texture buffer */ - copy_v4_v4(tf, rgba); - - /* output premultiplied float image, mf was already premultiplied */ - mul_v3_v3fl(bf, rgba, rgba[3]); - bf[3] = rgba[3]; - } - else { - unsigned char crgba[4]; - - /* handle byte pixel */ - unsigned char *b = (unsigned char *)ibuf->rect + (y * ibuf->x + x) * 4; - unsigned char *t = (unsigned char *)texibuf->rect + (y * texibuf->x + x) * 4; - - /* read from old texture buffer */ - if (use_texture_old) { - unsigned char *ot = (unsigned char *)oldtexibuf->rect + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; - crgba[0] = ot[0]; - crgba[1] = ot[1]; - crgba[2] = ot[2]; - crgba[3] = ot[3]; - } - else - rgba_float_to_uchar(crgba, rgba); - - /* write to new texture buffer */ - t[0] = crgba[0]; - t[1] = crgba[1]; - t[2] = crgba[2]; - t[3] = crgba[3]; - - /* write to brush image buffer */ - b[0] = crgba[0]; - b[1] = crgba[1]; - b[2] = crgba[2]; - b[3] = crgba[3]; - } - } - } + Scene *scene = painter->scene; + Brush *brush = painter->brush; + + const char *display_device = scene->display_settings.display_device; + struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + + rctf tex_mapping = painter->tex_mapping; + struct ImagePool *pool = painter->pool; + + bool use_color_correction = painter->cache.use_color_correction; + bool use_float = painter->cache.use_float; + bool is_texbrush = painter->cache.is_texbrush; + bool use_texture_old = (oldtexibuf != NULL); + + int x, y, thread = 0; + float brush_rgb[3]; + + ImBuf *ibuf = painter->cache.ibuf; + ImBuf *texibuf = painter->cache.texibuf; + + /* get brush color */ + if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { + paint_brush_color_get( + scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); + } + else { + brush_rgb[0] = 1.0f; + brush_rgb[1] = 1.0f; + brush_rgb[2] = 1.0f; + } + + /* fill pixels */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture and multiply with brush color */ + float texco[3], rgba[4]; + + if (!use_texture_old) { + if (is_texbrush) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + /* TODO(sergey): Support texture paint color space. */ + if (!use_float) { + IMB_colormanagement_scene_linear_to_display_v3(rgba, display); + } + mul_v3_v3(rgba, brush_rgb); + } + else { + copy_v3_v3(rgba, brush_rgb); + rgba[3] = 1.0f; + } + } + + if (use_float) { + /* handle float pixel */ + float *bf = ibuf->rect_float + (y * ibuf->x + x) * 4; + float *tf = texibuf->rect_float + (y * texibuf->x + x) * 4; + + /* read from old texture buffer */ + if (use_texture_old) { + const float *otf = oldtexibuf->rect_float + + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; + copy_v4_v4(rgba, otf); + } + + /* write to new texture buffer */ + copy_v4_v4(tf, rgba); + + /* output premultiplied float image, mf was already premultiplied */ + mul_v3_v3fl(bf, rgba, rgba[3]); + bf[3] = rgba[3]; + } + else { + unsigned char crgba[4]; + + /* handle byte pixel */ + unsigned char *b = (unsigned char *)ibuf->rect + (y * ibuf->x + x) * 4; + unsigned char *t = (unsigned char *)texibuf->rect + (y * texibuf->x + x) * 4; + + /* read from old texture buffer */ + if (use_texture_old) { + unsigned char *ot = (unsigned char *)oldtexibuf->rect + + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; + crgba[0] = ot[0]; + crgba[1] = ot[1]; + crgba[2] = ot[2]; + crgba[3] = ot[3]; + } + else + rgba_float_to_uchar(crgba, rgba); + + /* write to new texture buffer */ + t[0] = crgba[0]; + t[1] = crgba[1]; + t[2] = crgba[2]; + t[3] = crgba[3]; + + /* write to brush image buffer */ + b[0] = crgba[0]; + b[1] = crgba[1]; + b[2] = crgba[2]; + b[3] = crgba[3]; + } + } + } } /* update the brush image by trying to reuse the cached texture result. this * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +static void brush_painter_imbuf_partial_update(BrushPainter *painter, + const float pos[2], + int diameter) { - BrushPainterCache *cache = &painter->cache; - ImBuf *oldtexibuf, *ibuf; - int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - - /* create brush image buffer if it didn't exist yet */ - imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; - if (!cache->ibuf) - cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); - ibuf = cache->ibuf; - - /* create new texture image buffer with coordinates relative to old */ - oldtexibuf = cache->texibuf; - cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); - - if (oldtexibuf) { - srcx = srcy = 0; - w = oldtexibuf->x; - h = oldtexibuf->y; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); - - IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); - } - else { - srcx = srcy = 0; - destx = desty = 0; - w = h = 0; - } - - x1 = min_ii(destx, ibuf->x); - y1 = min_ii(desty, ibuf->y); - x2 = min_ii(destx + w, ibuf->x); - y2 = min_ii(desty + h, ibuf->y); - - /* blend existing texture in new position */ - if ((x1 < x2) && (y1 < y2)) - brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); - - if (oldtexibuf) - IMB_freeImBuf(oldtexibuf); - - /* sample texture in new areas */ - if ((0 < x1) && (0 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); - if ((x2 < ibuf->x) && (0 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); - if ((x1 < x2) && (0 < y1)) - brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); - if ((x1 < x2) && (y2 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); + BrushPainterCache *cache = &painter->cache; + ImBuf *oldtexibuf, *ibuf; + int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + /* create brush image buffer if it didn't exist yet */ + imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; + if (!cache->ibuf) + cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + ibuf = cache->ibuf; + + /* create new texture image buffer with coordinates relative to old */ + oldtexibuf = cache->texibuf; + cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + + if (oldtexibuf) { + srcx = srcy = 0; + w = oldtexibuf->x; + h = oldtexibuf->y; + destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + + IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); + } + else { + srcx = srcy = 0; + destx = desty = 0; + w = h = 0; + } + + x1 = min_ii(destx, ibuf->x); + y1 = min_ii(desty, ibuf->y); + x2 = min_ii(destx + w, ibuf->x); + y2 = min_ii(desty + h, ibuf->y); + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); + + if (oldtexibuf) + IMB_freeImBuf(oldtexibuf); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < ibuf->y)) + brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); + if ((x2 < ibuf->x) && (0 < ibuf->y)) + brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); + if ((x1 < x2) && (0 < y1)) + brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); + if ((x1 < x2) && (y2 < ibuf->y)) + brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); } -static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) +static void brush_painter_2d_tex_mapping(ImagePaintState *s, + int diameter, + const float startpos[2], + const float pos[2], + const float mouse[2], + int mapmode, + rctf *mapping) { - float invw = 1.0f / (float)s->canvas->x; - float invh = 1.0f / (float)s->canvas->y; - int xmin, ymin, xmax, ymax; - int ipos[2]; - - /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); - - if (mapmode == MTEX_MAP_MODE_STENCIL) { - /* map from view coordinates of brush to region coordinates */ - UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); - - /* output mapping from brush ibuf x/y to region coordinates */ - mapping->xmin = xmin; - mapping->ymin = ymin; - mapping->xmax = (xmax - xmin) / (float)diameter; - mapping->ymax = (ymax - ymin) / (float)diameter; - } - else if (mapmode == MTEX_MAP_MODE_3D) { - /* 3D mapping, just mapping to canvas 0..1 */ - mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f); - mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f); - mapping->xmax = 2.0f * invw; - mapping->ymax = 2.0f * invh; - } - else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { - /* view mapping */ - mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; - mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; - mapping->xmax = 1.0f; - mapping->ymax = 1.0f; - } - else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); - mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); - mapping->xmax = 1.0f; - mapping->ymax = 1.0f; - } + float invw = 1.0f / (float)s->canvas->x; + float invh = 1.0f / (float)s->canvas->y; + int xmin, ymin, xmax, ymax; + int ipos[2]; + + /* find start coordinate of brush in canvas */ + ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); + + if (mapmode == MTEX_MAP_MODE_STENCIL) { + /* map from view coordinates of brush to region coordinates */ + UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); + UI_view2d_view_to_region( + s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); + + /* output mapping from brush ibuf x/y to region coordinates */ + mapping->xmin = xmin; + mapping->ymin = ymin; + mapping->xmax = (xmax - xmin) / (float)diameter; + mapping->ymax = (ymax - ymin) / (float)diameter; + } + else if (mapmode == MTEX_MAP_MODE_3D) { + /* 3D mapping, just mapping to canvas 0..1 */ + mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f); + mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f); + mapping->xmax = 2.0f * invw; + mapping->ymax = 2.0f * invh; + } + else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { + /* view mapping */ + mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; + mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; + mapping->xmax = 1.0f; + mapping->ymax = 1.0f; + } + else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { + mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); + mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); + mapping->xmax = 1.0f; + mapping->ymax = 1.0f; + } } -static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2], float pressure, float distance, float size) +static void brush_painter_2d_refresh_cache(ImagePaintState *s, + BrushPainter *painter, + const float pos[2], + const float mouse[2], + float pressure, + float distance, + float size) { - const Scene *scene = painter->scene; - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - Brush *brush = painter->brush; - BrushPainterCache *cache = &painter->cache; - const int diameter = 2 * size; - - bool do_random = false; - bool do_partial_update = false; - bool update_color = ( - (brush->flag & BRUSH_USE_GRADIENT) && - ((ELEM(brush->gradient_stroke_mode, - BRUSH_GRADIENT_SPACING_REPEAT, - BRUSH_GRADIENT_SPACING_CLAMP)) || - (cache->last_pressure != pressure))); - float tex_rotation = -brush->mtex.rot; - float mask_rotation = -brush->mask_mtex.rot; - - painter->pool = BKE_image_pool_new(); - - /* determine how can update based on textures used */ - if (painter->cache.is_texbrush) { - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - tex_rotation += ups->brush_rotation; - } - else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - do_random = true; - else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) - do_partial_update = true; - - brush_painter_2d_tex_mapping( - s, diameter, painter->startpaintpos, pos, mouse, - brush->mtex.brush_map_mode, &painter->tex_mapping); - } - - if (painter->cache.is_maskbrush) { - bool renew_maxmask = false; - bool do_partial_update_mask = false; - /* invalidate case for all mapping modes */ - if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - mask_rotation += ups->brush_rotation_sec; - } - else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) { - renew_maxmask = true; - } - else if (!(brush->flag & BRUSH_ANCHORED)) { - do_partial_update_mask = true; - renew_maxmask = true; - } - /* explicitly disable partial update even if it has been enabled above */ - if (brush->mask_pressure) { - do_partial_update_mask = false; - renew_maxmask = true; - } - - 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; - } - - brush_painter_2d_tex_mapping( - s, diameter, painter->startpaintpos, pos, mouse, - brush->mask_mtex.brush_map_mode, &painter->mask_mapping); - - if (do_partial_update_mask) - brush_painter_mask_imbuf_partial_update(painter, pos, diameter); - else - cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); - cache->last_mask_rotation = mask_rotation; - } - } - - /* curve mask can only change if the size changes */ - if (diameter != cache->lastdiameter) { - if (cache->curve_mask) { - MEM_freeN(cache->curve_mask); - cache->curve_mask = NULL; - } - - cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); - } - - /* detect if we need to recreate image brush buffer */ - if ((diameter != cache->lastdiameter) || - (tex_rotation != cache->last_tex_rotation) || - do_random || - update_color) - { - if (cache->ibuf) { - IMB_freeImBuf(cache->ibuf); - cache->ibuf = NULL; - } - - if (do_partial_update) { - /* do partial update of texture */ - brush_painter_imbuf_partial_update(painter, pos, diameter); - } - else { - /* create brush from scratch */ - cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); - } - - cache->lastdiameter = diameter; - cache->last_tex_rotation = tex_rotation; - cache->last_pressure = pressure; - } - else if (do_partial_update) { - /* do only partial update of texture */ - int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); - int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); - - if ((dx != 0) || (dy != 0)) { - brush_painter_imbuf_partial_update(painter, pos, diameter); - } - } - - BKE_image_pool_free(painter->pool); - painter->pool = NULL; + const Scene *scene = painter->scene; + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + Brush *brush = painter->brush; + BrushPainterCache *cache = &painter->cache; + const int diameter = 2 * size; + + bool do_random = false; + bool do_partial_update = false; + bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && + ((ELEM(brush->gradient_stroke_mode, + BRUSH_GRADIENT_SPACING_REPEAT, + BRUSH_GRADIENT_SPACING_CLAMP)) || + (cache->last_pressure != pressure))); + float tex_rotation = -brush->mtex.rot; + float mask_rotation = -brush->mask_mtex.rot; + + painter->pool = BKE_image_pool_new(); + + /* determine how can update based on textures used */ + if (painter->cache.is_texbrush) { + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { + tex_rotation += ups->brush_rotation; + } + else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + do_random = true; + else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) + do_partial_update = true; + + brush_painter_2d_tex_mapping(s, + diameter, + painter->startpaintpos, + pos, + mouse, + brush->mtex.brush_map_mode, + &painter->tex_mapping); + } + + if (painter->cache.is_maskbrush) { + bool renew_maxmask = false; + bool do_partial_update_mask = false; + /* invalidate case for all mapping modes */ + if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { + mask_rotation += ups->brush_rotation_sec; + } + else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) { + renew_maxmask = true; + } + else if (!(brush->flag & BRUSH_ANCHORED)) { + do_partial_update_mask = true; + renew_maxmask = true; + } + /* explicitly disable partial update even if it has been enabled above */ + if (brush->mask_pressure) { + do_partial_update_mask = false; + renew_maxmask = true; + } + + 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; + } + + brush_painter_2d_tex_mapping(s, + diameter, + painter->startpaintpos, + pos, + mouse, + brush->mask_mtex.brush_map_mode, + &painter->mask_mapping); + + if (do_partial_update_mask) + brush_painter_mask_imbuf_partial_update(painter, pos, diameter); + else + cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); + cache->last_mask_rotation = mask_rotation; + } + } + + /* curve mask can only change if the size changes */ + if (diameter != cache->lastdiameter) { + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); + cache->curve_mask = NULL; + } + + cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); + } + + /* detect if we need to recreate image brush buffer */ + if ((diameter != cache->lastdiameter) || (tex_rotation != cache->last_tex_rotation) || + do_random || update_color) { + if (cache->ibuf) { + IMB_freeImBuf(cache->ibuf); + cache->ibuf = NULL; + } + + if (do_partial_update) { + /* do partial update of texture */ + brush_painter_imbuf_partial_update(painter, pos, diameter); + } + else { + /* create brush from scratch */ + cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); + } + + cache->lastdiameter = diameter; + cache->last_tex_rotation = tex_rotation; + cache->last_pressure = pressure; + } + else if (do_partial_update) { + /* do only partial update of texture */ + int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); + int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); + + if ((dx != 0) || (dy != 0)) { + brush_painter_imbuf_partial_update(painter, pos, diameter); + } + } + + BKE_image_pool_free(painter->pool); + painter->pool = NULL; } /* keep these functions in sync */ static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4]) { - if (ibuf->rect_float) { - const float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; - copy_v4_v4(r_rgb, rrgbf); - } - else { - unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; - straight_uchar_to_premul_float(r_rgb, rrgb); - } + if (ibuf->rect_float) { + const float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; + copy_v4_v4(r_rgb, rrgbf); + } + else { + unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; + straight_uchar_to_premul_float(r_rgb, rrgb); + } } -static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus, const float rgb[4]) +static void paint_2d_ibuf_rgb_set( + ImBuf *ibuf, int x, int y, const bool is_torus, const float rgb[4]) { - if (is_torus) { - x %= ibuf->x; - if (x < 0) x += ibuf->x; - y %= ibuf->y; - if (y < 0) y += ibuf->y; - } - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; - float map_alpha = (rgb[3] == 0.0f) ? rrgbf[3] : rrgbf[3] / rgb[3]; - - mul_v3_v3fl(rrgbf, rgb, map_alpha); - rrgbf[3] = rgb[3]; - } - else { - unsigned char straight[4]; - unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; - - premul_float_to_straight_uchar(straight, rgb); - rrgb[0] = straight[0]; - rrgb[1] = straight[1]; - rrgb[2] = straight[2]; - rrgb[3] = straight[3]; - } + if (is_torus) { + x %= ibuf->x; + if (x < 0) + x += ibuf->x; + y %= ibuf->y; + if (y < 0) + y += ibuf->y; + } + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; + float map_alpha = (rgb[3] == 0.0f) ? rrgbf[3] : rrgbf[3] / rgb[3]; + + mul_v3_v3fl(rrgbf, rgb, map_alpha); + rrgbf[3] = rgb[3]; + } + else { + unsigned char straight[4]; + unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; + + premul_float_to_straight_uchar(straight, rgb); + rrgb[0] = straight[0]; + rrgb[1] = straight[1]; + rrgb[2] = straight[2]; + rrgb[3] = straight[3]; + } } static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile) { - if (tile & PAINT_TILE_X) { - *x %= ibuf->x; - if (*x < 0) *x += ibuf->x; - } - if (tile & PAINT_TILE_Y) { - *y %= ibuf->y; - if (*y < 0) *y += ibuf->y; - } + if (tile & PAINT_TILE_X) { + *x %= ibuf->x; + if (*x < 0) + *x += ibuf->x; + } + if (tile & PAINT_TILE_Y) { + *y %= ibuf->y; + if (*y < 0) + *y += ibuf->y; + } } - static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w) { - float inrgb[4]; - - if (tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); - /* need to also do clipping here always since tiled coordinates - * are not always within bounds */ - if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) { - paint_2d_ibuf_rgb_get(ibuf, x, y, inrgb); - } - else return 0; - - mul_v4_fl(inrgb, w); - add_v4_v4(outrgb, inrgb); - - return w; + float inrgb[4]; + + if (tile) + paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); + /* need to also do clipping here always since tiled coordinates + * are not always within bounds */ + if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) { + paint_2d_ibuf_rgb_get(ibuf, x, y, inrgb); + } + else + return 0; + + mul_v4_fl(inrgb, w); + add_v4_v4(outrgb, inrgb); + + return w; } -static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) +static void paint_2d_lift_soften( + ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) { - bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); - float threshold = s->brush->sharp_threshold; - int x, y, xi, yi, xo, yo, xk, yk; - float count; - int out_off[2], in_off[2], dim[2]; - int diff_pos[2]; - float outrgb[4]; - float rgba[4]; - BlurKernel *kernel = s->blurkernel; - - dim[0] = ibufb->x; - dim[1] = ibufb->y; - in_off[0] = pos[0]; - in_off[1] = pos[1]; - out_off[0] = out_off[1] = 0; - - if (!tile) { - IMB_rectclip( - ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], - &out_off[1], &dim[0], &dim[1]); - - if ((dim[0] == 0) || (dim[1] == 0)) - return; - } - - /* find offset inside mask buffers to sample them */ - sub_v2_v2v2_int(diff_pos, out_off, in_off); - - for (y = 0; y < dim[1]; y++) { - for (x = 0; x < dim[0]; x++) { - /* get input pixel */ - xi = in_off[0] + x; - yi = in_off[1] + y; - - count = 0.0; - if (tile) { - paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); - if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) - paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); - else - zero_v4(rgba); - } - else { - /* coordinates have been clipped properly here, it should be safe to do this */ - paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); - } - zero_v4(outrgb); - - for (yk = 0; yk < kernel->side; yk++) { - for (xk = 0; xk < kernel->side; xk++) { - count += paint_2d_ibuf_add_if( - ibuf, xi + xk - kernel->pixel_len, - yi + yk - kernel->pixel_len, outrgb, tile, - kernel->wdata[xk + yk * kernel->side]); - } - } - - if (count > 0.0f) { - mul_v4_fl(outrgb, 1.0f / (float)count); - - if (sharpen) { - /* subtract blurred image from normal image gives high pass filter */ - sub_v3_v3v3(outrgb, rgba, outrgb); - - /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid - * colored speckles appearing in final image, and also to check for threshold */ - outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); - if (fabsf(outrgb[0]) > threshold) { - float mask = BKE_brush_alpha_get(s->scene, s->brush); - float alpha = rgba[3]; - rgba[3] = outrgb[3] = mask; - - /* add to enhance edges */ - blend_color_add_float(outrgb, rgba, outrgb); - outrgb[3] = alpha; - } - else - copy_v4_v4(outrgb, rgba); - } - } - else - copy_v4_v4(outrgb, rgba); - /* write into brush buffer */ - xo = out_off[0] + x; - yo = out_off[1] + y; - paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); - } - } + bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); + float threshold = s->brush->sharp_threshold; + int x, y, xi, yi, xo, yo, xk, yk; + float count; + int out_off[2], in_off[2], dim[2]; + int diff_pos[2]; + float outrgb[4]; + float rgba[4]; + BlurKernel *kernel = s->blurkernel; + + dim[0] = ibufb->x; + dim[1] = ibufb->y; + in_off[0] = pos[0]; + in_off[1] = pos[1]; + out_off[0] = out_off[1] = 0; + + if (!tile) { + IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); + + if ((dim[0] == 0) || (dim[1] == 0)) + return; + } + + /* find offset inside mask buffers to sample them */ + sub_v2_v2v2_int(diff_pos, out_off, in_off); + + for (y = 0; y < dim[1]; y++) { + for (x = 0; x < dim[0]; x++) { + /* get input pixel */ + xi = in_off[0] + x; + yi = in_off[1] + y; + + count = 0.0; + if (tile) { + paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); + if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) + paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); + else + zero_v4(rgba); + } + else { + /* coordinates have been clipped properly here, it should be safe to do this */ + paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); + } + zero_v4(outrgb); + + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + count += paint_2d_ibuf_add_if(ibuf, + xi + xk - kernel->pixel_len, + yi + yk - kernel->pixel_len, + outrgb, + tile, + kernel->wdata[xk + yk * kernel->side]); + } + } + + if (count > 0.0f) { + mul_v4_fl(outrgb, 1.0f / (float)count); + + if (sharpen) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(outrgb, rgba, outrgb); + + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); + if (fabsf(outrgb[0]) > threshold) { + float mask = BKE_brush_alpha_get(s->scene, s->brush); + float alpha = rgba[3]; + rgba[3] = outrgb[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(outrgb, rgba, outrgb); + outrgb[3] = alpha; + } + else + copy_v4_v4(outrgb, rgba); + } + } + else + copy_v4_v4(outrgb, rgba); + /* write into brush buffer */ + xo = out_off[0] + x; + yo = out_off[1] + y; + paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); + } + } } -static void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height) +static void paint_2d_set_region( + ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height) { - region->destx = destx; - region->desty = desty; - region->srcx = srcx; - region->srcy = srcy; - region->width = width; - region->height = height; + region->destx = destx; + region->desty = desty; + region->srcx = srcx; + region->srcy = srcy; + region->width = width; + region->height = height; } -static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short tile) +static int paint_2d_torus_split_region(ImagePaintRegion region[4], + ImBuf *dbuf, + ImBuf *sbuf, + short tile) { - int destx = region->destx; - int desty = region->desty; - int srcx = region->srcx; - int srcy = region->srcy; - int width = region->width; - int height = region->height; - int origw, origh, w, h, tot = 0; - - /* convert destination and source coordinates to be within image */ - if (tile & PAINT_TILE_X) { - destx = destx % dbuf->x; - if (destx < 0) destx += dbuf->x; - srcx = srcx % sbuf->x; - if (srcx < 0) srcx += sbuf->x; - } - if (tile & PAINT_TILE_Y) { - desty = desty % dbuf->y; - if (desty < 0) desty += dbuf->y; - srcy = srcy % sbuf->y; - if (srcy < 0) srcy += sbuf->y; - } - /* clip width of blending area to destination imbuf, to avoid writing the - * same pixel twice */ - origw = w = (width > dbuf->x) ? dbuf->x : width; - origh = h = (height > dbuf->y) ? dbuf->y : height; - - /* clip within image */ - IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); - paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); - - /* do 3 other rects if needed */ - if ((tile & PAINT_TILE_X) && w < origw) - paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); - if ((tile & PAINT_TILE_Y) && h < origh) - paint_2d_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); - if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) - paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h); - - return tot; + int destx = region->destx; + int desty = region->desty; + int srcx = region->srcx; + int srcy = region->srcy; + int width = region->width; + int height = region->height; + int origw, origh, w, h, tot = 0; + + /* convert destination and source coordinates to be within image */ + if (tile & PAINT_TILE_X) { + destx = destx % dbuf->x; + if (destx < 0) + destx += dbuf->x; + srcx = srcx % sbuf->x; + if (srcx < 0) + srcx += sbuf->x; + } + if (tile & PAINT_TILE_Y) { + desty = desty % dbuf->y; + if (desty < 0) + desty += dbuf->y; + srcy = srcy % sbuf->y; + if (srcy < 0) + srcy += sbuf->y; + } + /* clip width of blending area to destination imbuf, to avoid writing the + * same pixel twice */ + origw = w = (width > dbuf->x) ? dbuf->x : width; + origh = h = (height > dbuf->y) ? dbuf->y : height; + + /* clip within image */ + IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); + paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); + + /* do 3 other rects if needed */ + if ((tile & PAINT_TILE_X) && w < origw) + paint_2d_set_region( + ®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); + if ((tile & PAINT_TILE_Y) && h < origh) + paint_2d_set_region( + ®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); + if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) + paint_2d_set_region(®ion[tot++], + (destx + w) % dbuf->x, + (desty + h) % dbuf->y, + (srcx + w) % sbuf->x, + (srcy + h) % sbuf->y, + origw - w, + origh - h); + + return tot; } static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short tile) { - ImagePaintRegion region[4]; - int a, tot; - - paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); - tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile); - - for (a = 0; a < tot; a++) - IMB_rectblend( - ibufb, ibufb, ibuf, NULL, NULL, NULL, 0, region[a].destx, region[a].desty, - region[a].destx, region[a].desty, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, IMB_BLEND_COPY, false); + ImagePaintRegion region[4]; + int a, tot; + + paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); + tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile); + + for (a = 0; a < tot; a++) + IMB_rectblend(ibufb, + ibufb, + ibuf, + NULL, + NULL, + NULL, + 0, + region[a].destx, + region[a].desty, + region[a].destx, + region[a].desty, + region[a].srcx, + region[a].srcy, + region[a].width, + region[a].height, + IMB_BLEND_COPY, + false); } static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) { - /* note: allocImbuf returns zero'd memory, so regions outside image will - * have zero alpha, and hence not be blended onto the image */ - int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; - ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); - - IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); - IMB_rectblend( - clonebuf, clonebuf, ibufb, NULL, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, - IMB_BLEND_COPY_ALPHA, false); - IMB_rectblend( - clonebuf, clonebuf, ibuf, NULL, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, - IMB_BLEND_COPY_RGB, false); - - return clonebuf; + /* note: allocImbuf returns zero'd memory, so regions outside image will + * have zero alpha, and hence not be blended onto the image */ + int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; + ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); + + IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); + IMB_rectblend(clonebuf, + clonebuf, + ibufb, + NULL, + NULL, + NULL, + 0, + destx, + desty, + destx, + desty, + destx, + desty, + w, + h, + IMB_BLEND_COPY_ALPHA, + false); + IMB_rectblend(clonebuf, + clonebuf, + ibuf, + NULL, + NULL, + NULL, + 0, + destx, + desty, + destx, + desty, + srcx, + srcy, + w, + h, + IMB_BLEND_COPY_RGB, + false); + + return clonebuf; } static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) { - ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); - ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); + ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); + ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); } -static void paint_2d_do_making_brush( - ImagePaintState *s, - ImagePaintRegion *region, - unsigned short *curveb, - unsigned short *texmaskb, - ImBuf *frombuf, - float mask_max, - short blend, - int tilex, int tiley, - int tilew, int tileh) +static void paint_2d_do_making_brush(ImagePaintState *s, + ImagePaintRegion *region, + unsigned short *curveb, + unsigned short *texmaskb, + ImBuf *frombuf, + float mask_max, + short blend, + int tilex, + int tiley, + int tilew, + int tileh) { - ImBuf tmpbuf; - IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); - - ListBase *undo_tiles = ED_image_undo_get_tiles(); - - for (int ty = tiley; ty <= tileh; ty++) { - for (int tx = tilex; tx <= tilew; tx++) { - /* retrieve original pixels + mask from undo buffer */ - unsigned short *mask; - int origx = region->destx - tx * IMAPAINT_TILE_SIZE; - int origy = region->desty - ty * IMAPAINT_TILE_SIZE; - - if (s->canvas->rect_float) - tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); - else - tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); - - IMB_rectblend( - s->canvas, &tmpbuf, frombuf, mask, - curveb, texmaskb, mask_max, - region->destx, region->desty, - origx, origy, - region->srcx, region->srcy, - region->width, region->height, - blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); - } - } + ImBuf tmpbuf; + IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + + ListBase *undo_tiles = ED_image_undo_get_tiles(); + + for (int ty = tiley; ty <= tileh; ty++) { + for (int tx = tilex; tx <= tilew; tx++) { + /* retrieve original pixels + mask from undo buffer */ + unsigned short *mask; + int origx = region->destx - tx * IMAPAINT_TILE_SIZE; + int origy = region->desty - ty * IMAPAINT_TILE_SIZE; + + if (s->canvas->rect_float) + tmpbuf.rect_float = image_undo_find_tile( + undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + else + tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + + IMB_rectblend(s->canvas, + &tmpbuf, + frombuf, + mask, + curveb, + texmaskb, + mask_max, + region->destx, + region->desty, + origx, + origy, + region->srcx, + region->srcy, + region->width, + region->height, + blend, + ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); + } + } } typedef struct Paint2DForeachData { - ImagePaintState *s; - ImagePaintRegion *region; - unsigned short *curveb; - unsigned short *texmaskb; - ImBuf *frombuf; - float mask_max; - short blend; - int tilex; - int tilew; + ImagePaintState *s; + ImagePaintRegion *region; + unsigned short *curveb; + unsigned short *texmaskb; + ImBuf *frombuf; + float mask_max; + short blend; + int tilex; + int tilew; } Paint2DForeachData; -static void paint_2d_op_foreach_do( - void *__restrict data_v, - const int iter, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void paint_2d_op_foreach_do(void *__restrict data_v, + const int iter, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - Paint2DForeachData *data = (Paint2DForeachData *)data_v; - paint_2d_do_making_brush( - data->s, data->region, data->curveb, - data->texmaskb, data->frombuf, data->mask_max, - data->blend, - data->tilex, iter, - data->tilew, iter); + Paint2DForeachData *data = (Paint2DForeachData *)data_v; + paint_2d_do_making_brush(data->s, + data->region, + data->curveb, + data->texmaskb, + data->frombuf, + data->mask_max, + data->blend, + data->tilex, + iter, + data->tilew, + iter); } -static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) +static int paint_2d_op(void *state, + ImBuf *ibufb, + unsigned short *curveb, + unsigned short *texmaskb, + const float lastpos[2], + const float pos[2]) { - ImagePaintState *s = ((ImagePaintState *)state); - ImBuf *clonebuf = NULL, *frombuf; - ImagePaintRegion region[4]; - short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); - short blend = s->blend; - const float *offset = s->brush->clone.offset; - float liftpos[2]; - float mask_max = BKE_brush_alpha_get(s->scene, s->brush); - int bpos[2], blastpos[2], bliftpos[2]; - int a, tot; - - paint_2d_convert_brushco(ibufb, pos, bpos); - - /* lift from canvas */ - if (s->tool == PAINT_TOOL_SOFTEN) { - paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile); - blend = IMB_BLEND_INTERPOLATE; - } - else if (s->tool == PAINT_TOOL_SMEAR) { - if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) - return 0; - - paint_2d_convert_brushco(ibufb, lastpos, blastpos); - paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile); - blend = IMB_BLEND_INTERPOLATE; - } - else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { - liftpos[0] = pos[0] - offset[0] * s->canvas->x; - liftpos[1] = pos[1] - offset[1] * s->canvas->y; - - paint_2d_convert_brushco(ibufb, liftpos, bliftpos); - clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); - } - - frombuf = (clonebuf) ? clonebuf : ibufb; - - if (tile) { - paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile); - } - else { - paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = 1; - } - - /* blend into canvas */ - for (a = 0; a < tot; a++) { - ED_imapaint_dirty_region( - s->image, s->canvas, - region[a].destx, region[a].desty, - region[a].width, region[a].height, true); - - if (s->do_masking) { - /* masking, find original pixels tiles from undo buffer to composite over */ - int tilex, tiley, tilew, tileh; - - imapaint_region_tiles( - s->canvas, region[a].destx, region[a].desty, - region[a].width, region[a].height, - &tilex, &tiley, &tilew, &tileh); - - if (tiley == tileh) { - paint_2d_do_making_brush( - s, ®ion[a], curveb, texmaskb, frombuf, - mask_max, blend, tilex, tiley, tilew, tileh); - } - else { - Paint2DForeachData data; - data.s = s; - data.region = ®ion[a]; - data.curveb = curveb; - data.texmaskb = texmaskb; - data.frombuf = frombuf; - data.mask_max = mask_max; - data.blend = blend; - data.tilex = tilex; - data.tilew = tilew; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range( - tiley, tileh + 1, &data, - paint_2d_op_foreach_do, - &settings); - - } - } - else { - /* no masking, composite brush directly onto canvas */ - IMB_rectblend_threaded( - s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, - region[a].destx, region[a].desty, - region[a].destx, region[a].desty, - region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend, false); - } - } - - if (clonebuf) IMB_freeImBuf(clonebuf); - - return 1; + ImagePaintState *s = ((ImagePaintState *)state); + ImBuf *clonebuf = NULL, *frombuf; + ImagePaintRegion region[4]; + short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); + short blend = s->blend; + const float *offset = s->brush->clone.offset; + float liftpos[2]; + float mask_max = BKE_brush_alpha_get(s->scene, s->brush); + int bpos[2], blastpos[2], bliftpos[2]; + int a, tot; + + paint_2d_convert_brushco(ibufb, pos, bpos); + + /* lift from canvas */ + if (s->tool == PAINT_TOOL_SOFTEN) { + paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile); + blend = IMB_BLEND_INTERPOLATE; + } + else if (s->tool == PAINT_TOOL_SMEAR) { + if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) + return 0; + + paint_2d_convert_brushco(ibufb, lastpos, blastpos); + paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile); + blend = IMB_BLEND_INTERPOLATE; + } + else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { + liftpos[0] = pos[0] - offset[0] * s->canvas->x; + liftpos[1] = pos[1] - offset[1] * s->canvas->y; + + paint_2d_convert_brushco(ibufb, liftpos, bliftpos); + clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); + } + + frombuf = (clonebuf) ? clonebuf : ibufb; + + if (tile) { + paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); + tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile); + } + else { + paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); + tot = 1; + } + + /* blend into canvas */ + for (a = 0; a < tot; a++) { + ED_imapaint_dirty_region(s->image, + s->canvas, + region[a].destx, + region[a].desty, + region[a].width, + region[a].height, + true); + + if (s->do_masking) { + /* masking, find original pixels tiles from undo buffer to composite over */ + int tilex, tiley, tilew, tileh; + + imapaint_region_tiles(s->canvas, + region[a].destx, + region[a].desty, + region[a].width, + region[a].height, + &tilex, + &tiley, + &tilew, + &tileh); + + if (tiley == tileh) { + paint_2d_do_making_brush( + s, ®ion[a], curveb, texmaskb, frombuf, mask_max, blend, tilex, tiley, tilew, tileh); + } + else { + Paint2DForeachData data; + data.s = s; + data.region = ®ion[a]; + data.curveb = curveb; + data.texmaskb = texmaskb; + data.frombuf = frombuf; + data.mask_max = mask_max; + data.blend = blend; + data.tilex = tilex; + data.tilew = tilew; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(tiley, tileh + 1, &data, paint_2d_op_foreach_do, &settings); + } + } + else { + /* no masking, composite brush directly onto canvas */ + IMB_rectblend_threaded(s->canvas, + s->canvas, + frombuf, + NULL, + curveb, + texmaskb, + mask_max, + region[a].destx, + region[a].desty, + region[a].destx, + region[a].desty, + region[a].srcx, + region[a].srcy, + region[a].width, + region[a].height, + blend, + false); + } + } + + if (clonebuf) + IMB_freeImBuf(clonebuf); + + return 1; } - static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); - - /* verify that we can paint and set canvas */ - if (ima == NULL) { - return 0; - } - else if (BKE_image_has_packedfile(ima) && ima->rr) { - s->warnpackedfile = ima->id.name + 2; - return 0; - } - else if (ibuf && ibuf->channels != 4) { - s->warnmultifile = ima->id.name + 2; - return 0; - } - else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) - return 0; - - s->image = ima; - s->canvas = ibuf; - - /* set clone canvas */ - if (s->tool == PAINT_TOOL_CLONE) { - ima = s->brush->clone.image; - ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); - - if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { - BKE_image_release_ibuf(ima, ibuf, NULL); - BKE_image_release_ibuf(s->image, s->canvas, NULL); - return 0; - } - - s->clonecanvas = ibuf; - - /* temporarily add float rect for cloning */ - if (s->canvas->rect_float && !s->clonecanvas->rect_float) { - IMB_float_from_rect(s->clonecanvas); - } - else if (!s->canvas->rect_float && !s->clonecanvas->rect) - IMB_rect_from_float(s->clonecanvas); - } - - /* set masking */ - s->do_masking = paint_use_opacity_masking(s->brush); - - return 1; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); + + /* verify that we can paint and set canvas */ + if (ima == NULL) { + return 0; + } + else if (BKE_image_has_packedfile(ima) && ima->rr) { + s->warnpackedfile = ima->id.name + 2; + return 0; + } + else if (ibuf && ibuf->channels != 4) { + s->warnmultifile = ima->id.name + 2; + return 0; + } + else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) + return 0; + + s->image = ima; + s->canvas = ibuf; + + /* set clone canvas */ + if (s->tool == PAINT_TOOL_CLONE) { + ima = s->brush->clone.image; + ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); + + if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { + BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(s->image, s->canvas, NULL); + return 0; + } + + s->clonecanvas = ibuf; + + /* temporarily add float rect for cloning */ + if (s->canvas->rect_float && !s->clonecanvas->rect_float) { + IMB_float_from_rect(s->clonecanvas); + } + else if (!s->canvas->rect_float && !s->clonecanvas->rect) + IMB_rect_from_float(s->clonecanvas); + } + + /* set masking */ + s->do_masking = paint_use_opacity_masking(s->brush); + + return 1; } static void paint_2d_canvas_free(ImagePaintState *s) { - BKE_image_release_ibuf(s->image, s->canvas, NULL); - BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); + BKE_image_release_ibuf(s->image, s->canvas, NULL); + BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); - if (s->blurkernel) { - paint_delete_blur_kernel(s->blurkernel); - MEM_freeN(s->blurkernel); - } + if (s->blurkernel) { + paint_delete_blur_kernel(s->blurkernel); + MEM_freeN(s->blurkernel); + } - image_undo_remove_masks(); + image_undo_remove_masks(); } -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size) +void paint_2d_stroke(void *ps, + const float prev_mval[2], + const float mval[2], + const bool eraser, + float pressure, + float distance, + float size) { - float newuv[2], olduv[2]; - ImagePaintState *s = ps; - BrushPainter *painter = s->painter; - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); - const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA); + float newuv[2], olduv[2]; + ImagePaintState *s = ps; + BrushPainter *painter = s->painter; + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); + const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA); - if (!ibuf) - return; + if (!ibuf) + return; - s->blend = s->brush->blend; - if (eraser) - s->blend = IMB_BLEND_ERASE_ALPHA; + s->blend = s->brush->blend; + if (eraser) + s->blend = IMB_BLEND_ERASE_ALPHA; - UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); - UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]); + UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); + UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]); - newuv[0] *= ibuf->x; - newuv[1] *= ibuf->y; + newuv[0] *= ibuf->x; + newuv[1] *= ibuf->y; - olduv[0] *= ibuf->x; - olduv[1] *= ibuf->y; + olduv[0] *= ibuf->x; + olduv[1] *= ibuf->y; - if (painter->firsttouch) { - float startuv[2]; + if (painter->firsttouch) { + float startuv[2]; - UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]); + UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]); - /* paint exactly once on first touch */ - painter->startpaintpos[0] = startuv[0] * ibuf->x; - painter->startpaintpos[1] = startuv[1] * ibuf->y; + /* paint exactly once on first touch */ + painter->startpaintpos[0] = startuv[0] * ibuf->x; + painter->startpaintpos[1] = startuv[1] * ibuf->y; - painter->firsttouch = 0; - copy_v2_v2(painter->lastpaintpos, newuv); - } - else { - copy_v2_v2(painter->lastpaintpos, olduv); - } + painter->firsttouch = 0; + copy_v2_v2(painter->lastpaintpos, newuv); + } + else { + copy_v2_v2(painter->lastpaintpos, olduv); + } - /* OCIO_TODO: float buffers are now always linear, so always use color correction - * this should probably be changed when texture painting color space is supported - */ - brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); + /* OCIO_TODO: float buffers are now always linear, so always use color correction + * this should probably be changed when texture painting color space is supported + */ + brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); - brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); + brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); - if (paint_2d_op(s, painter->cache.ibuf, painter->cache.curve_mask, painter->cache.tex_mask, olduv, newuv)) - s->need_redraw = true; + if (paint_2d_op(s, + painter->cache.ibuf, + painter->cache.curve_mask, + painter->cache.tex_mask, + olduv, + newuv)) + s->need_redraw = true; - BKE_image_release_ibuf(s->image, ibuf, NULL); + BKE_image_release_ibuf(s->image, ibuf, NULL); } void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - Brush *brush = BKE_paint_brush(&settings->imapaint.paint); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); - ImagePaintState *s = MEM_callocN(sizeof(ImagePaintState), "ImagePaintState"); + ImagePaintState *s = MEM_callocN(sizeof(ImagePaintState), "ImagePaintState"); - s->sima = CTX_wm_space_image(C); - s->v2d = &CTX_wm_region(C)->v2d; - s->scene = scene; - s->screen = CTX_wm_screen(C); + s->sima = CTX_wm_space_image(C); + s->v2d = &CTX_wm_region(C)->v2d; + s->scene = scene; + s->screen = CTX_wm_screen(C); - s->brush = brush; - s->tool = brush->imagepaint_tool; - s->blend = brush->blend; + s->brush = brush; + s->tool = brush->imagepaint_tool; + s->blend = brush->blend; - s->image = s->sima->image; - s->symmetry = settings->imapaint.paint.symmetry_flags; + s->image = s->sima->image; + s->symmetry = settings->imapaint.paint.symmetry_flags; - if (!paint_2d_canvas_set(s, s->image)) { - if (s->warnmultifile) - BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); - if (s->warnpackedfile) - BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); + if (!paint_2d_canvas_set(s, s->image)) { + if (s->warnmultifile) + BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); + if (s->warnpackedfile) + BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); - MEM_freeN(s); - return NULL; - } + MEM_freeN(s); + return NULL; + } - if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { - s->blurkernel = paint_new_blur_kernel(brush, false); - } + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + s->blurkernel = paint_new_blur_kernel(brush, false); + } - paint_brush_init_tex(s->brush); + paint_brush_init_tex(s->brush); - /* create painter */ - s->painter = brush_painter_2d_new(scene, s->brush, mode == BRUSH_STROKE_INVERT); + /* create painter */ + s->painter = brush_painter_2d_new(scene, s->brush, mode == BRUSH_STROKE_INVERT); - return s; + return s; } void paint_2d_redraw(const bContext *C, void *ps, bool final) { - ImagePaintState *s = ps; - - if (s->need_redraw) { - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); - - imapaint_image_update(s->sima, s->image, ibuf, false); - ED_imapaint_clear_partial_redraw(); - - BKE_image_release_ibuf(s->image, ibuf, NULL); - - s->need_redraw = false; - } - else if (!final) { - return; - } - - if (final) { - if (s->image && !(s->sima && s->sima->lock)) - GPU_free_image(s->image); - - /* compositor listener deals with updating */ - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); - DEG_id_tag_update(&s->image->id, 0); - } - else { - if (!s->sima || !s->sima->lock) - ED_region_tag_redraw(CTX_wm_region(C)); - else - WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); - } + ImagePaintState *s = ps; + + if (s->need_redraw) { + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); + + imapaint_image_update(s->sima, s->image, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(s->image, ibuf, NULL); + + s->need_redraw = false; + } + else if (!final) { + return; + } + + if (final) { + if (s->image && !(s->sima && s->sima->lock)) + GPU_free_image(s->image); + + /* compositor listener deals with updating */ + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + DEG_id_tag_update(&s->image->id, 0); + } + else { + if (!s->sima || !s->sima->lock) + ED_region_tag_redraw(CTX_wm_region(C)); + else + WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); + } } void paint_2d_stroke_done(void *ps) { - ImagePaintState *s = ps; + ImagePaintState *s = ps; - paint_2d_canvas_free(s); - brush_painter_2d_free(s->painter); - paint_brush_exit_tex(s->brush); + paint_2d_canvas_free(s); + brush_painter_2d_free(s->painter); + paint_brush_exit_tex(s->brush); - MEM_freeN(s); + MEM_freeN(s); } -static void paint_2d_fill_add_pixel_byte( - const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, - const float color[4], float threshold_sq) +static void paint_2d_fill_add_pixel_byte(const int x_px, + const int y_px, + ImBuf *ibuf, + BLI_Stack *stack, + BLI_bitmap *touched, + const float color[4], + float threshold_sq) { - size_t coordinate; + size_t coordinate; - if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) - return; + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; - coordinate = ((size_t)y_px) * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; - if (!BLI_BITMAP_TEST(touched, coordinate)) { - float color_f[4]; - unsigned char *color_b = (unsigned char *)(ibuf->rect + coordinate); - rgba_uchar_to_float(color_f, color_b); - straight_to_premul_v4(color_f); + if (!BLI_BITMAP_TEST(touched, coordinate)) { + float color_f[4]; + unsigned char *color_b = (unsigned char *)(ibuf->rect + coordinate); + rgba_uchar_to_float(color_f, color_b); + straight_to_premul_v4(color_f); - if (len_squared_v4v4(color_f, color) <= threshold_sq) { - BLI_stack_push(stack, &coordinate); - } - BLI_BITMAP_SET(touched, coordinate, true); - } + if (len_squared_v4v4(color_f, color) <= threshold_sq) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } } -static void paint_2d_fill_add_pixel_float( - const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, - const float color[4], float threshold_sq) +static void paint_2d_fill_add_pixel_float(const int x_px, + const int y_px, + ImBuf *ibuf, + BLI_Stack *stack, + BLI_bitmap *touched, + const float color[4], + float threshold_sq) { - size_t coordinate; + size_t coordinate; - if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) - return; + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; - coordinate = ((size_t)y_px) * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; - if (!BLI_BITMAP_TEST(touched, coordinate)) { - if (len_squared_v4v4(ibuf->rect_float + 4 * coordinate, color) <= threshold_sq) { - BLI_stack_push(stack, &coordinate); - } - BLI_BITMAP_SET(touched, coordinate, true); - } + if (!BLI_BITMAP_TEST(touched, coordinate)) { + if (len_squared_v4v4(ibuf->rect_float + 4 * coordinate, color) <= threshold_sq) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } } /* this function expects linear space color values */ void paint_2d_bucket_fill( - const bContext *C, const float color[3], Brush *br, - const float mouse_init[2], - void *ps) + const bContext *C, const float color[3], Brush *br, const float mouse_init[2], void *ps) { - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = sima->image; - - ImagePaintState *s = ps; - - ImBuf *ibuf; - int x_px, y_px; - unsigned int color_b; - float color_f[4]; - float strength = br ? br->alpha : 1.0f; - - bool do_float; - - if (!ima) - return; - - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); - - if (!ibuf) - return; - - do_float = (ibuf->rect_float != NULL); - /* first check if our image is float. If it is not we should correct the color to - * be in gamma space. strictly speaking this is not correct, but blender does not paint - * byte images in linear space */ - if (!do_float) { - linearrgb_to_srgb_uchar3((unsigned char *)&color_b, color); - *(((char *)&color_b) + 3) = strength * 255; - } - else { - copy_v3_v3(color_f, color); - color_f[3] = strength; - } - - if (!mouse_init || !br) { - /* first case, no image UV, fill the whole image */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - - if (do_float) { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - blend_color_mix_float( - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), color_f); - } - } - } - else { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - blend_color_mix_byte( - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), (unsigned char *)&color_b); - } - } - } - } - else { - /* second case, start sweeping the neighboring pixels, looking for pixels whose - * value is within the brush fill threshold from the fill color */ - BLI_Stack *stack; - BLI_bitmap *touched; - size_t coordinate; - int width = ibuf->x; - float image_init[2]; - int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; - float pixel_color[4]; - /* We are comparing to sum of three squared values - * (assumed in range [0,1]), so need to multiply... */ - float threshold_sq = br->fill_threshold * br->fill_threshold * 3; - - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); - - x_px = image_init[0] * ibuf->x; - y_px = image_init[1] * ibuf->y; - - if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) { - BKE_image_release_ibuf(ima, ibuf, NULL); - return; - } - - /* change image invalidation method later */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - - stack = BLI_stack_new(sizeof(size_t), __func__); - touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); - - coordinate = (((size_t)y_px) * ibuf->x + x_px); - - if (do_float) { - copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); - } - else { - int pixel_color_b = *(ibuf->rect + coordinate); - rgba_uchar_to_float(pixel_color, (unsigned char *)&pixel_color_b); - straight_to_premul_v4(pixel_color); - } - - BLI_stack_push(stack, &coordinate); - BLI_BITMAP_SET(touched, coordinate, true); - - if (do_float) { - while (!BLI_stack_is_empty(stack)) { - BLI_stack_pop(stack, &coordinate); - - IMB_blend_color_float( - ibuf->rect_float + 4 * (coordinate), - ibuf->rect_float + 4 * (coordinate), - color_f, br->blend); - - /* reconstruct the coordinates here */ - x_px = coordinate % width; - y_px = coordinate / width; - - paint_2d_fill_add_pixel_float(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - - if (x_px > maxx) - maxx = x_px; - if (x_px < minx) - minx = x_px; - if (y_px > maxy) - maxy = y_px; - if (x_px > miny) - miny = y_px; - } - } - else { - while (!BLI_stack_is_empty(stack)) { - BLI_stack_pop(stack, &coordinate); - - IMB_blend_color_byte( - (unsigned char *)(ibuf->rect + coordinate), - (unsigned char *)(ibuf->rect + coordinate), - (unsigned char *)&color_b, br->blend); - - /* reconstruct the coordinates here */ - x_px = coordinate % width; - y_px = coordinate / width; - - paint_2d_fill_add_pixel_byte(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - - if (x_px > maxx) - maxx = x_px; - if (x_px < minx) - minx = x_px; - if (y_px > maxy) - maxy = y_px; - if (x_px > miny) - miny = y_px; - } - } - - MEM_freeN(touched); - BLI_stack_free(stack); - } - - imapaint_image_update(sima, ima, ibuf, false); - ED_imapaint_clear_partial_redraw(); - - BKE_image_release_ibuf(ima, ibuf, NULL); - - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float strength = br ? br->alpha : 1.0f; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + do_float = (ibuf->rect_float != NULL); + /* first check if our image is float. If it is not we should correct the color to + * be in gamma space. strictly speaking this is not correct, but blender does not paint + * byte images in linear space */ + if (!do_float) { + linearrgb_to_srgb_uchar3((unsigned char *)&color_b, color); + *(((char *)&color_b) + 3) = strength * 255; + } + else { + copy_v3_v3(color_f, color); + color_f[3] = strength; + } + + if (!mouse_init || !br) { + /* first case, no image UV, fill the whole image */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + color_f); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)&color_b); + } + } + } + } + else { + /* second case, start sweeping the neighboring pixels, looking for pixels whose + * value is within the brush fill threshold from the fill color */ + BLI_Stack *stack; + BLI_bitmap *touched; + size_t coordinate; + int width = ibuf->x; + float image_init[2]; + int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; + float pixel_color[4]; + /* We are comparing to sum of three squared values + * (assumed in range [0,1]), so need to multiply... */ + float threshold_sq = br->fill_threshold * br->fill_threshold * 3; + + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + x_px = image_init[0] * ibuf->x; + y_px = image_init[1] * ibuf->y; + + if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) { + BKE_image_release_ibuf(ima, ibuf, NULL); + return; + } + + /* change image invalidation method later */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + + stack = BLI_stack_new(sizeof(size_t), __func__); + touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); + + coordinate = (((size_t)y_px) * ibuf->x + x_px); + + if (do_float) { + copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); + } + else { + int pixel_color_b = *(ibuf->rect + coordinate); + rgba_uchar_to_float(pixel_color, (unsigned char *)&pixel_color_b); + straight_to_premul_v4(pixel_color); + } + + BLI_stack_push(stack, &coordinate); + BLI_BITMAP_SET(touched, coordinate, true); + + if (do_float) { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_float(ibuf->rect_float + 4 * (coordinate), + ibuf->rect_float + 4 * (coordinate), + color_f, + br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_float( + x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + else { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_byte((unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)&color_b, + br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_byte( + x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + + MEM_freeN(touched); + BLI_stack_free(stack); + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } void paint_2d_gradient_fill( - const bContext *C, Brush *br, - const float mouse_init[2], const float mouse_final[2], - void *ps) + const bContext *C, Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps) { - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = sima->image; - ImagePaintState *s = ps; - - ImBuf *ibuf; - int x_px, y_px; - unsigned int color_b; - float color_f[4]; - float image_init[2], image_final[2]; - float tangent[2]; - float line_len_sq_inv, line_len; - - bool do_float; - - if (!ima) - return; - - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); - - if (!ibuf) - return; - - UI_view2d_region_to_view(s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); - - image_final[0] *= ibuf->x; - image_final[1] *= ibuf->y; - - image_init[0] *= ibuf->x; - image_init[1] *= ibuf->y; - - /* some math to get needed gradient variables */ - sub_v2_v2v2(tangent, image_final, image_init); - line_len = len_squared_v2(tangent); - line_len_sq_inv = 1.0f / line_len; - line_len = sqrtf(line_len); - - do_float = (ibuf->rect_float != NULL); - - /* this will be substituted by something else when selection is available */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - - if (do_float) { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - float f; - float p[2] = {x_px - image_init[0], y_px - image_init[1]}; - - switch (br->gradient_fill_mode) { - case BRUSH_GRADIENT_LINEAR: - { - f = dot_v2v2(p, tangent) * line_len_sq_inv; - break; - } - case BRUSH_GRADIENT_RADIAL: - default: - { - f = len_v2(p) / line_len; - break; - } - } - BKE_colorband_evaluate(br->gradient, f, color_f); - /* convert to premultiplied */ - mul_v3_fl(color_f, color_f[3]); - color_f[3] *= br->alpha; - IMB_blend_color_float( - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), - color_f, br->blend); - } - } - } - else { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - float f; - float p[2] = {x_px - image_init[0], y_px - image_init[1]}; - - switch (br->gradient_fill_mode) { - case BRUSH_GRADIENT_LINEAR: - { - f = dot_v2v2(p, tangent) * line_len_sq_inv; - break; - } - case BRUSH_GRADIENT_RADIAL: - default: - { - f = len_v2(p) / line_len; - break; - } - } - - BKE_colorband_evaluate(br->gradient, f, color_f); - linearrgb_to_srgb_v3_v3(color_f, color_f); - rgba_float_to_uchar((unsigned char *)&color_b, color_f); - ((unsigned char *)&color_b)[3] *= br->alpha; - IMB_blend_color_byte( - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), - (unsigned char *)&color_b, br->blend); - } - } - } - - imapaint_image_update(sima, ima, ibuf, false); - ED_imapaint_clear_partial_redraw(); - - BKE_image_release_ibuf(ima, ibuf, NULL); - - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float image_init[2], image_final[2]; + float tangent[2]; + float line_len_sq_inv, line_len; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + UI_view2d_region_to_view( + s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + image_final[0] *= ibuf->x; + image_final[1] *= ibuf->y; + + image_init[0] *= ibuf->x; + image_init[1] *= ibuf->y; + + /* some math to get needed gradient variables */ + sub_v2_v2v2(tangent, image_final, image_init); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrtf(line_len); + + do_float = (ibuf->rect_float != NULL); + + /* this will be substituted by something else when selection is available */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + default: { + f = len_v2(p) / line_len; + break; + } + } + BKE_colorband_evaluate(br->gradient, f, color_f); + /* convert to premultiplied */ + mul_v3_fl(color_f, color_f[3]); + color_f[3] *= br->alpha; + IMB_blend_color_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + color_f, + br->blend); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + default: { + f = len_v2(p) / line_len; + break; + } + } + + BKE_colorband_evaluate(br->gradient, f, color_f); + linearrgb_to_srgb_v3_v3(color_f, color_f); + rgba_float_to_uchar((unsigned char *)&color_b, color_f); + ((unsigned char *)&color_b)[3] *= br->alpha; + IMB_blend_color_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)&color_b, + br->blend); + } + } + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 45ae3e9dc11..7a99f819913 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -45,7 +45,6 @@ #include "BLT_translation.h" - #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -114,7 +113,7 @@ static void partial_redraw_array_init(ImagePaintPartialRedraw *pr); /* unit_float_to_uchar_clamp as inline function */ BLI_INLINE unsigned char f_to_char(const float val) { - return unit_float_to_uchar_clamp(val); + return unit_float_to_uchar_clamp(val); } /* ProjectionPaint defines */ @@ -131,58 +130,56 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_BUCKET_RECT_MAX 256 #define PROJ_BOUNDBOX_DIV 8 -#define PROJ_BOUNDBOX_SQUARED (PROJ_BOUNDBOX_DIV * PROJ_BOUNDBOX_DIV) +#define PROJ_BOUNDBOX_SQUARED (PROJ_BOUNDBOX_DIV * PROJ_BOUNDBOX_DIV) //#define PROJ_DEBUG_PAINT 1 //#define PROJ_DEBUG_NOSEAMBLEED 1 //#define PROJ_DEBUG_PRINT_CLIP 1 #define PROJ_DEBUG_WINCLIP 1 - #ifndef PROJ_DEBUG_NOSEAMBLEED /* projectFaceSeamFlags options */ -//#define PROJ_FACE_IGNORE (1<<0) /* When the face is hidden, backfacing or occluded */ -//#define PROJ_FACE_INIT (1<<1) /* When we have initialized the faces data */ +//#define PROJ_FACE_IGNORE (1<<0) /* When the face is hidden, backfacing or occluded */ +//#define PROJ_FACE_INIT (1<<1) /* When we have initialized the faces data */ /* If this face has a seam on any of its edges. */ -#define PROJ_FACE_SEAM0 (1 << 0) -#define PROJ_FACE_SEAM1 (1 << 1) -#define PROJ_FACE_SEAM2 (1 << 2) +# define PROJ_FACE_SEAM0 (1 << 0) +# define PROJ_FACE_SEAM1 (1 << 1) +# define PROJ_FACE_SEAM2 (1 << 2) -#define PROJ_FACE_NOSEAM0 (1 << 4) -#define PROJ_FACE_NOSEAM1 (1 << 5) -#define PROJ_FACE_NOSEAM2 (1 << 6) +# define PROJ_FACE_NOSEAM0 (1 << 4) +# define PROJ_FACE_NOSEAM1 (1 << 5) +# define PROJ_FACE_NOSEAM2 (1 << 6) /* If the seam is completely initialized, including adjecent seams. */ -#define PROJ_FACE_SEAM_INIT0 (1 << 8) -#define PROJ_FACE_SEAM_INIT1 (1 << 9) -#define PROJ_FACE_SEAM_INIT2 (1 << 10) +# define PROJ_FACE_SEAM_INIT0 (1 << 8) +# define PROJ_FACE_SEAM_INIT1 (1 << 9) +# define PROJ_FACE_SEAM_INIT2 (1 << 10) -#define PROJ_FACE_DEGENERATE (1 << 12) +# define PROJ_FACE_DEGENERATE (1 << 12) /* face winding */ -#define PROJ_FACE_WINDING_INIT 1 -#define PROJ_FACE_WINDING_CW 2 +# define PROJ_FACE_WINDING_INIT 1 +# define PROJ_FACE_WINDING_CW 2 /* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams * as this number approaches 1.0f the likelihood increases of float precision errors where * it is occluded by an adjacent face */ -#define PROJ_FACE_SCALE_SEAM 0.99f -#endif /* PROJ_DEBUG_NOSEAMBLEED */ - +# define PROJ_FACE_SCALE_SEAM 0.99f +#endif /* PROJ_DEBUG_NOSEAMBLEED */ -#define PROJ_SRC_VIEW 1 -#define PROJ_SRC_IMAGE_CAM 2 +#define PROJ_SRC_VIEW 1 +#define PROJ_SRC_IMAGE_CAM 2 #define PROJ_SRC_IMAGE_VIEW 3 -#define PROJ_SRC_VIEW_FILL 4 +#define PROJ_SRC_VIEW_FILL 4 #define PROJ_VIEW_DATA_ID "view_data" /* viewmat + winmat + clip_start + clip_end + is_ortho */ #define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) -#define PROJ_BUCKET_NULL 0 -#define PROJ_BUCKET_INIT (1 << 0) -// #define PROJ_BUCKET_CLONE_INIT (1<<1) +#define PROJ_BUCKET_NULL 0 +#define PROJ_BUCKET_INIT (1 << 0) +// #define PROJ_BUCKET_CLONE_INIT (1<<1) /* used for testing doubles, if a point is on a line etc */ #define PROJ_GEOM_TOLERANCE 0.00075f @@ -198,605 +195,595 @@ BLI_INLINE unsigned char f_to_char(const float val) * their imbufs, etc, in 1 array, When using threads this array is copied for each thread * because 'partRedrawRect' and 'touch' values would not be thread safe */ typedef struct ProjPaintImage { - Image *ima; - ImBuf *ibuf; - ImagePaintPartialRedraw *partRedrawRect; - /** Only used to build undo tiles during painting. */ - volatile void **undoRect; - /** The mask accumulation must happen on canvas, not on space screen bucket. - * Here we store the mask rectangle. */ - unsigned short **maskRect; - /** Store flag to enforce validation of undo rectangle. */ - bool **valid; - bool touch; + Image *ima; + ImBuf *ibuf; + ImagePaintPartialRedraw *partRedrawRect; + /** Only used to build undo tiles during painting. */ + volatile void **undoRect; + /** The mask accumulation must happen on canvas, not on space screen bucket. + * Here we store the mask rectangle. */ + unsigned short **maskRect; + /** Store flag to enforce validation of undo rectangle. */ + bool **valid; + bool touch; } ProjPaintImage; /** * Handle for stroke (operator customdata) */ typedef struct ProjStrokeHandle { - /* Support for painting from multiple views at once, - * currently used to implement symmetry painting, - * we can assume at least the first is set while painting. */ - struct ProjPaintState *ps_views[8]; - int ps_views_tot; - int symmetry_flags; + /* Support for painting from multiple views at once, + * currently used to implement symmetry painting, + * we can assume at least the first is set while painting. */ + struct ProjPaintState *ps_views[8]; + int ps_views_tot; + int symmetry_flags; - int orig_brush_size; + int orig_brush_size; - bool need_redraw; + bool need_redraw; - /* trick to bypass regular paint and allow clone picking */ - bool is_clone_cursor_pick; + /* trick to bypass regular paint and allow clone picking */ + bool is_clone_cursor_pick; - /* In ProjPaintState, only here for convenience */ - Scene *scene; - Brush *brush; + /* In ProjPaintState, only here for convenience */ + Scene *scene; + Brush *brush; } ProjStrokeHandle; typedef struct LoopSeamData { - float seam_uvs[2][2]; - float seam_puvs[2][2]; - float corner_dist_sq[2]; + float seam_uvs[2][2]; + float seam_puvs[2][2]; + float corner_dist_sq[2]; } LoopSeamData; /* Main projection painting struct passed to all projection painting functions */ typedef struct ProjPaintState { - View3D *v3d; - RegionView3D *rv3d; - ARegion *ar; - Depsgraph *depsgraph; - Scene *scene; - /* PROJ_SRC_**** */ - int source; - - /* the paint color. It can change depending of inverted mode or not */ - float paint_color[3]; - float paint_color_linear[3]; - float dither; - - Brush *brush; - short tool, blend, mode; - - float brush_size; - Object *ob; - /* for symmetry, we need to store modified object matrix */ - float obmat[4][4]; - float obmat_imat[4][4]; - /* end similarities with ImagePaintState */ - - Image *stencil_ima; - Image *canvas_ima; - Image *clone_ima; - float stencil_value; - - /* projection painting only */ - /** for multithreading, the first item is sometimes used for non threaded cases too. */ - MemArena *arena_mt[BLENDER_MAX_THREADS]; - /** screen sized 2D array, each pixel has a linked list of ProjPixel's */ - LinkNode **bucketRect; - /** bucketRect aligned array linkList of faces overlapping each bucket. */ - LinkNode **bucketFaces; - /** store if the bucks have been initialized. */ - unsigned char *bucketFlags; - - /** store options per vert, now only store if the vert is pointing away from the view. */ - char *vertFlags; - /** The size of the bucket grid, the grid span's screenMin/screenMax - * so you can paint outsize the screen or with 2 brushes at once. */ - int buckets_x; - int buckets_y; - - /** result of project_paint_pixel_sizeof(), constant per stroke. */ - int pixel_sizeof; - - /** size of projectImages array. */ - int image_tot; - - /** verts projected into floating point screen space. */ - float (*screenCoords)[4]; - /** 2D bounds for mesh verts on the screen's plane (screenspace). */ - float screenMin[2]; - float screenMax[2]; - /** Calculated from screenMin & screenMax. */ - float screen_width; - float screen_height; - /** from the carea or from the projection render. */ - int winx, winy; - - /* options for projection painting */ - bool do_layer_clone; - bool do_layer_stencil; - bool do_layer_stencil_inv; - bool do_stencil_brush; - bool do_material_slots; - - /** Use raytraced occlusion? - ortherwise will paint right through to the back. */ - bool do_occlude; - /** ignore faces with normals pointing away, - * skips a lot of raycasts if your normals are correctly flipped. */ - bool do_backfacecull; - /** mask out pixels based on their normals. */ - bool do_mask_normal; - /** mask out pixels based on cavity. */ - bool do_mask_cavity; - /** what angle to mask at. */ - float normal_angle; - /** cos(normal_angle), faster to compare. */ - float normal_angle__cos; - float normal_angle_inner; - float normal_angle_inner__cos; - /** difference between normal_angle and normal_angle_inner, for easy access. */ - float normal_angle_range; - - /** quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ - bool do_face_sel; - bool is_ortho; - /** the object is negative scaled. */ - bool is_flip_object; - /** use masking during painting. Some operations such as airbrush may disable. */ - bool do_masking; - /** only to avoid running. */ - bool is_texbrush; - /** mask brush is applied before masking. */ - bool is_maskbrush; + View3D *v3d; + RegionView3D *rv3d; + ARegion *ar; + Depsgraph *depsgraph; + Scene *scene; + /* PROJ_SRC_**** */ + int source; + + /* the paint color. It can change depending of inverted mode or not */ + float paint_color[3]; + float paint_color_linear[3]; + float dither; + + Brush *brush; + short tool, blend, mode; + + float brush_size; + Object *ob; + /* for symmetry, we need to store modified object matrix */ + float obmat[4][4]; + float obmat_imat[4][4]; + /* end similarities with ImagePaintState */ + + Image *stencil_ima; + Image *canvas_ima; + Image *clone_ima; + float stencil_value; + + /* projection painting only */ + /** for multithreading, the first item is sometimes used for non threaded cases too. */ + MemArena *arena_mt[BLENDER_MAX_THREADS]; + /** screen sized 2D array, each pixel has a linked list of ProjPixel's */ + LinkNode **bucketRect; + /** bucketRect aligned array linkList of faces overlapping each bucket. */ + LinkNode **bucketFaces; + /** store if the bucks have been initialized. */ + unsigned char *bucketFlags; + + /** store options per vert, now only store if the vert is pointing away from the view. */ + char *vertFlags; + /** The size of the bucket grid, the grid span's screenMin/screenMax + * so you can paint outsize the screen or with 2 brushes at once. */ + int buckets_x; + int buckets_y; + + /** result of project_paint_pixel_sizeof(), constant per stroke. */ + int pixel_sizeof; + + /** size of projectImages array. */ + int image_tot; + + /** verts projected into floating point screen space. */ + float (*screenCoords)[4]; + /** 2D bounds for mesh verts on the screen's plane (screenspace). */ + float screenMin[2]; + float screenMax[2]; + /** Calculated from screenMin & screenMax. */ + float screen_width; + float screen_height; + /** from the carea or from the projection render. */ + int winx, winy; + + /* options for projection painting */ + bool do_layer_clone; + bool do_layer_stencil; + bool do_layer_stencil_inv; + bool do_stencil_brush; + bool do_material_slots; + + /** Use raytraced occlusion? - ortherwise will paint right through to the back. */ + bool do_occlude; + /** ignore faces with normals pointing away, + * skips a lot of raycasts if your normals are correctly flipped. */ + bool do_backfacecull; + /** mask out pixels based on their normals. */ + bool do_mask_normal; + /** mask out pixels based on cavity. */ + bool do_mask_cavity; + /** what angle to mask at. */ + float normal_angle; + /** cos(normal_angle), faster to compare. */ + float normal_angle__cos; + float normal_angle_inner; + float normal_angle_inner__cos; + /** difference between normal_angle and normal_angle_inner, for easy access. */ + float normal_angle_range; + + /** quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ + bool do_face_sel; + bool is_ortho; + /** the object is negative scaled. */ + bool is_flip_object; + /** use masking during painting. Some operations such as airbrush may disable. */ + bool do_masking; + /** only to avoid running. */ + bool is_texbrush; + /** mask brush is applied before masking. */ + bool is_maskbrush; #ifndef PROJ_DEBUG_NOSEAMBLEED - float seam_bleed_px; - float seam_bleed_px_sq; + float seam_bleed_px; + float seam_bleed_px_sq; #endif - /* clone vars */ - float cloneOffset[2]; - - /** Projection matrix, use for getting screen coords. */ - float projectMat[4][4]; - /** inverse of projectMat. */ - float projectMatInv[4][4]; - /** View vector, use for do_backfacecull and for ray casting with an ortho viewport. */ - float viewDir[3]; - /** View location in object relative 3D space, so can compare to verts. */ - float viewPos[3]; - float clip_start, clip_end; - - /* reproject vars */ - Image *reproject_image; - ImBuf *reproject_ibuf; - bool reproject_ibuf_free_float; - bool reproject_ibuf_free_uchar; - - /* threads */ - int thread_tot; - int bucketMin[2]; - int bucketMax[2]; - /** must lock threads while accessing these. */ - int context_bucket_x, context_bucket_y; - - struct CurveMapping *cavity_curve; - BlurKernel *blurkernel; - - - - /* -------------------------------------------------------------------- */ - /* Vars shared between multiple views (keep last) */ - /** - * This data is owned by ``ProjStrokeHandle.ps_views[0]``, - * all other views re-use the data. - */ + /* clone vars */ + float cloneOffset[2]; + + /** Projection matrix, use for getting screen coords. */ + float projectMat[4][4]; + /** inverse of projectMat. */ + float projectMatInv[4][4]; + /** View vector, use for do_backfacecull and for ray casting with an ortho viewport. */ + float viewDir[3]; + /** View location in object relative 3D space, so can compare to verts. */ + float viewPos[3]; + float clip_start, clip_end; + + /* reproject vars */ + Image *reproject_image; + ImBuf *reproject_ibuf; + bool reproject_ibuf_free_float; + bool reproject_ibuf_free_uchar; + + /* threads */ + int thread_tot; + int bucketMin[2]; + int bucketMax[2]; + /** must lock threads while accessing these. */ + int context_bucket_x, context_bucket_y; + + struct CurveMapping *cavity_curve; + BlurKernel *blurkernel; + + /* -------------------------------------------------------------------- */ + /* Vars shared between multiple views (keep last) */ + /** + * This data is owned by ``ProjStrokeHandle.ps_views[0]``, + * all other views re-use the data. + */ #define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src) \ - MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user) + MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user) -#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) \ - MEMSET_STRUCT_AFTER(ps, 0, is_shared_user) +#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) MEMSET_STRUCT_AFTER(ps, 0, is_shared_user) - bool is_shared_user; + bool is_shared_user; - ProjPaintImage *projImages; - /** cavity amount for vertices. */ - float *cavities; + ProjPaintImage *projImages; + /** cavity amount for vertices. */ + float *cavities; #ifndef PROJ_DEBUG_NOSEAMBLEED - /** store info about faces, if they are initialized etc*/ - ushort *faceSeamFlags; - /** save the winding of the face in uv space, - * helps as an extra validation step for seam detection. */ - char *faceWindingFlags; - /** expanded UVs for faces to use as seams. */ - LoopSeamData (*loopSeamData); - /** Only needed for when seam_bleed_px is enabled, use to find UV seams. */ - LinkNode **vertFaces; - /** Seams per vert, to find adjacent seams. */ - ListBase *vertSeams; + /** store info about faces, if they are initialized etc*/ + ushort *faceSeamFlags; + /** save the winding of the face in uv space, + * helps as an extra validation step for seam detection. */ + char *faceWindingFlags; + /** expanded UVs for faces to use as seams. */ + LoopSeamData(*loopSeamData); + /** Only needed for when seam_bleed_px is enabled, use to find UV seams. */ + LinkNode **vertFaces; + /** Seams per vert, to find adjacent seams. */ + ListBase *vertSeams; #endif - SpinLock *tile_lock; - - Mesh *me_eval; - bool me_eval_free; - int totlooptri_eval; - int totloop_eval; - int totpoly_eval; - int totedge_eval; - int totvert_eval; - - const MVert *mvert_eval; - const MEdge *medge_eval; - const MPoly *mpoly_eval; - const MLoop *mloop_eval; - const MLoopTri *mlooptri_eval; - - const MLoopUV *mloopuv_stencil_eval; - - /** - * \note These UV layers are aligned to \a mpoly_eval - * but each pointer references the start of the layer, - * so a loop indirection is needed as well. - */ - const MLoopUV **poly_to_loop_uv; - /** other UV map, use for cloning between layers. */ - const MLoopUV **poly_to_loop_uv_clone; - - /* Actual material for each index, either from object or Mesh datablock... */ - Material **mat_array; - - bool use_colormanagement; + SpinLock *tile_lock; + + Mesh *me_eval; + bool me_eval_free; + int totlooptri_eval; + int totloop_eval; + int totpoly_eval; + int totedge_eval; + int totvert_eval; + + const MVert *mvert_eval; + const MEdge *medge_eval; + const MPoly *mpoly_eval; + const MLoop *mloop_eval; + const MLoopTri *mlooptri_eval; + + const MLoopUV *mloopuv_stencil_eval; + + /** + * \note These UV layers are aligned to \a mpoly_eval + * but each pointer references the start of the layer, + * so a loop indirection is needed as well. + */ + const MLoopUV **poly_to_loop_uv; + /** other UV map, use for cloning between layers. */ + const MLoopUV **poly_to_loop_uv_clone; + + /* Actual material for each index, either from object or Mesh datablock... */ + Material **mat_array; + + bool use_colormanagement; } ProjPaintState; typedef union pixelPointer { - /** float buffer. */ - float *f_pt; - /** 2 ways to access a char buffer. */ - unsigned int *uint_pt; - unsigned char *ch_pt; + /** float buffer. */ + float *f_pt; + /** 2 ways to access a char buffer. */ + unsigned int *uint_pt; + unsigned char *ch_pt; } PixelPointer; typedef union pixelStore { - unsigned char ch[4]; - unsigned int uint; - float f[4]; + unsigned char ch[4]; + unsigned int uint; + float f[4]; } PixelStore; typedef struct ProjPixel { - /** the floating point screen projection of this pixel. */ - float projCoSS[2]; - float worldCoSS[3]; + /** the floating point screen projection of this pixel. */ + float projCoSS[2]; + float worldCoSS[3]; - short x_px, y_px; + short x_px, y_px; - /** if anyone wants to paint onto more than 65535 images they can bite me. */ - unsigned short image_index; - unsigned char bb_cell_index; + /** if anyone wants to paint onto more than 65535 images they can bite me. */ + unsigned short image_index; + unsigned char bb_cell_index; - /* for various reasons we may want to mask out painting onto this pixel */ - unsigned short mask; + /* for various reasons we may want to mask out painting onto this pixel */ + unsigned short mask; - /* Only used when the airbrush is disabled. - * Store the max mask value to avoid painting over an area with a lower opacity - * with an advantage that we can avoid touching the pixel at all, if the - * new mask value is lower then mask_accum */ - unsigned short *mask_accum; + /* Only used when the airbrush is disabled. + * Store the max mask value to avoid painting over an area with a lower opacity + * with an advantage that we can avoid touching the pixel at all, if the + * new mask value is lower then mask_accum */ + unsigned short *mask_accum; - /* horrible hack, store tile valid flag pointer here to re-validate tiles - * used for anchored and drag-dot strokes */ - bool *valid; + /* horrible hack, store tile valid flag pointer here to re-validate tiles + * used for anchored and drag-dot strokes */ + bool *valid; - PixelPointer origColor; - PixelStore newColor; - PixelPointer pixel; + PixelPointer origColor; + PixelStore newColor; + PixelPointer pixel; } ProjPixel; typedef struct ProjPixelClone { - struct ProjPixel __pp; - PixelStore clonepx; + struct ProjPixel __pp; + PixelStore clonepx; } ProjPixelClone; /* undo tile pushing */ typedef struct { - SpinLock *lock; - bool masked; - unsigned short tile_width; - ImBuf **tmpibuf; - ProjPaintImage *pjima; + SpinLock *lock; + bool masked; + unsigned short tile_width; + ImBuf **tmpibuf; + ProjPaintImage *pjima; } TileInfo; typedef struct VertSeam { - struct VertSeam *next, *prev; - int tri; - uint loop; - float angle; - bool normal_cw; - float uv[2]; + struct VertSeam *next, *prev; + int tri; + uint loop; + float angle; + bool normal_cw; + float uv[2]; } VertSeam; - /* -------------------------------------------------------------------- */ /** \name MLoopTri accessor functions. * \{ */ BLI_INLINE const MPoly *ps_tri_index_to_mpoly(const ProjPaintState *ps, int tri_index) { - return &ps->mpoly_eval[ps->mlooptri_eval[tri_index].poly]; + return &ps->mpoly_eval[ps->mlooptri_eval[tri_index].poly]; } #define PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) \ - ps->mloop_eval[lt->tri[0]].v, \ - ps->mloop_eval[lt->tri[1]].v, \ - ps->mloop_eval[lt->tri[2]].v, + ps->mloop_eval[lt->tri[0]].v, ps->mloop_eval[lt->tri[1]].v, ps->mloop_eval[lt->tri[2]].v, #define PS_LOOPTRI_AS_UV_3(uvlayer, lt) \ - uvlayer[lt->poly][lt->tri[0]].uv, \ - uvlayer[lt->poly][lt->tri[1]].uv, \ - uvlayer[lt->poly][lt->tri[2]].uv, + uvlayer[lt->poly][lt->tri[0]].uv, uvlayer[lt->poly][lt->tri[1]].uv, \ + uvlayer[lt->poly][lt->tri[2]].uv, -#define PS_LOOPTRI_ASSIGN_UV_3(uv_tri, uvlayer, lt) { \ - (uv_tri)[0] = uvlayer[lt->poly][lt->tri[0]].uv; \ - (uv_tri)[1] = uvlayer[lt->poly][lt->tri[1]].uv; \ - (uv_tri)[2] = uvlayer[lt->poly][lt->tri[2]].uv; \ -} ((void)0) +#define PS_LOOPTRI_ASSIGN_UV_3(uv_tri, uvlayer, lt) \ + { \ + (uv_tri)[0] = uvlayer[lt->poly][lt->tri[0]].uv; \ + (uv_tri)[1] = uvlayer[lt->poly][lt->tri[1]].uv; \ + (uv_tri)[2] = uvlayer[lt->poly][lt->tri[2]].uv; \ + } \ + ((void)0) /** \} */ - - /* Finish projection painting structs */ static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - return ma ? ma->texpaintslot + ma->paint_active_slot : NULL; + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + return ma ? ma->texpaintslot + ma->paint_active_slot : NULL; } static Image *project_paint_face_paint_image(const ProjPaintState *ps, int tri_index) { - if (ps->do_stencil_brush) { - return ps->stencil_ima; - } - else { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL; - return slot ? slot->ima : ps->canvas_ima; - } + if (ps->do_stencil_brush) { + return ps->stencil_ima; + } + else { + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL; + return slot ? slot->ima : ps->canvas_ima; + } } static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; } static Image *project_paint_face_clone_image(const ProjPaintState *ps, int tri_index) { - const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); - Material *ma = ps->mat_array[mp->mat_nr]; - TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; - return slot ? slot->ima : ps->clone_ima; + const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); + Material *ma = ps->mat_array[mp->mat_nr]; + TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; + return slot ? slot->ima : ps->clone_ima; } /* fast projection bucket array lookup, use the safe version for bound checking */ static int project_bucket_offset(const ProjPaintState *ps, const float projCoSS[2]) { - /* If we were not dealing with screenspace 2D coords we could simple do... - * ps->bucketRect[x + (y*ps->buckets_y)] */ - - /* please explain? - * projCoSS[0] - ps->screenMin[0] : zero origin - * ... / ps->screen_width : range from 0.0 to 1.0 - * ... * ps->buckets_x : use as a bucket index - * - * Second multiplication does similar but for vertical offset - */ - return ( (int)(((projCoSS[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x)) + - (((int)(((projCoSS[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y)) * ps->buckets_x); + /* If we were not dealing with screenspace 2D coords we could simple do... + * ps->bucketRect[x + (y*ps->buckets_y)] */ + + /* please explain? + * projCoSS[0] - ps->screenMin[0] : zero origin + * ... / ps->screen_width : range from 0.0 to 1.0 + * ... * ps->buckets_x : use as a bucket index + * + * Second multiplication does similar but for vertical offset + */ + return ((int)(((projCoSS[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x)) + + (((int)(((projCoSS[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y)) * + ps->buckets_x); } static int project_bucket_offset_safe(const ProjPaintState *ps, const float projCoSS[2]) { - int bucket_index = project_bucket_offset(ps, projCoSS); - - if (bucket_index < 0 || bucket_index >= ps->buckets_x * ps->buckets_y) { - return -1; - } - else { - return bucket_index; - } + int bucket_index = project_bucket_offset(ps, projCoSS); + + if (bucket_index < 0 || bucket_index >= ps->buckets_x * ps->buckets_y) { + return -1; + } + else { + return bucket_index; + } } static float VecZDepthOrtho( - const float pt[2], - const float v1[3], const float v2[3], const float v3[3], - float w[3]) + const float pt[2], const float v1[3], const float v2[3], const float v3[3], float w[3]) { - barycentric_weights_v2(v1, v2, v3, pt, w); - return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]); + barycentric_weights_v2(v1, v2, v3, pt, w); + return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]); } static float VecZDepthPersp( - const float pt[2], - const float v1[4], const float v2[4], const float v3[4], - float w[3]) + const float pt[2], const float v1[4], const float v2[4], const float v3[4], float w[3]) { - float wtot_inv, wtot; - float w_tmp[3]; - - barycentric_weights_v2_persp(v1, v2, v3, pt, w); - /* for the depth we need the weights to match what - * barycentric_weights_v2 would return, in this case its easiest just to - * undo the 4th axis division and make it unit-sum - * - * don't call barycentric_weights_v2() because our callers expect 'w' - * to be weighted from the perspective */ - w_tmp[0] = w[0] * v1[3]; - w_tmp[1] = w[1] * v2[3]; - w_tmp[2] = w[2] * v3[3]; - - wtot = w_tmp[0] + w_tmp[1] + w_tmp[2]; - - if (wtot != 0.0f) { - wtot_inv = 1.0f / wtot; - - w_tmp[0] = w_tmp[0] * wtot_inv; - w_tmp[1] = w_tmp[1] * wtot_inv; - w_tmp[2] = w_tmp[2] * wtot_inv; - } - else /* dummy values for zero area face */ - w_tmp[0] = w_tmp[1] = w_tmp[2] = 1.0f / 3.0f; - /* done mimicking barycentric_weights_v2() */ - - return (v1[2] * w_tmp[0]) + (v2[2] * w_tmp[1]) + (v3[2] * w_tmp[2]); + float wtot_inv, wtot; + float w_tmp[3]; + + barycentric_weights_v2_persp(v1, v2, v3, pt, w); + /* for the depth we need the weights to match what + * barycentric_weights_v2 would return, in this case its easiest just to + * undo the 4th axis division and make it unit-sum + * + * don't call barycentric_weights_v2() because our callers expect 'w' + * to be weighted from the perspective */ + w_tmp[0] = w[0] * v1[3]; + w_tmp[1] = w[1] * v2[3]; + w_tmp[2] = w[2] * v3[3]; + + wtot = w_tmp[0] + w_tmp[1] + w_tmp[2]; + + if (wtot != 0.0f) { + wtot_inv = 1.0f / wtot; + + w_tmp[0] = w_tmp[0] * wtot_inv; + w_tmp[1] = w_tmp[1] * wtot_inv; + w_tmp[2] = w_tmp[2] * wtot_inv; + } + else /* dummy values for zero area face */ + w_tmp[0] = w_tmp[1] = w_tmp[2] = 1.0f / 3.0f; + /* done mimicking barycentric_weights_v2() */ + + return (v1[2] * w_tmp[0]) + (v2[2] * w_tmp[1]) + (v3[2] * w_tmp[2]); } - /* Return the top-most face index that the screen space coord 'pt' touches (or -1) */ -static int project_paint_PickFace( - const ProjPaintState *ps, const float pt[2], - float w[3]) +static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], float w[3]) { - LinkNode *node; - float w_tmp[3]; - int bucket_index; - int best_tri_index = -1; - float z_depth_best = FLT_MAX, z_depth; - - bucket_index = project_bucket_offset_safe(ps, pt); - if (bucket_index == -1) - return -1; - - - - /* we could return 0 for 1 face buckets, as long as this function assumes - * that the point its testing is only every originated from an existing face */ - - for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - const int tri_index = POINTER_AS_INT(node->link); - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *vtri_ss[3] = { - ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], - }; - - - if (isect_point_tri_v2(pt, UNPACK3(vtri_ss))) { - if (ps->is_ortho) { - z_depth = VecZDepthOrtho(pt, UNPACK3(vtri_ss), w_tmp); - } - else { - z_depth = VecZDepthPersp(pt, UNPACK3(vtri_ss), w_tmp); - } - - if (z_depth < z_depth_best) { - best_tri_index = tri_index; - z_depth_best = z_depth; - copy_v3_v3(w, w_tmp); - } - } - } - - /** will be -1 or a valid face. */ - return best_tri_index; + LinkNode *node; + float w_tmp[3]; + int bucket_index; + int best_tri_index = -1; + float z_depth_best = FLT_MAX, z_depth; + + bucket_index = project_bucket_offset_safe(ps, pt); + if (bucket_index == -1) + return -1; + + /* we could return 0 for 1 face buckets, as long as this function assumes + * that the point its testing is only every originated from an existing face */ + + for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { + const int tri_index = POINTER_AS_INT(node->link); + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *vtri_ss[3] = { + ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], + }; + + if (isect_point_tri_v2(pt, UNPACK3(vtri_ss))) { + if (ps->is_ortho) { + z_depth = VecZDepthOrtho(pt, UNPACK3(vtri_ss), w_tmp); + } + else { + z_depth = VecZDepthPersp(pt, UNPACK3(vtri_ss), w_tmp); + } + + if (z_depth < z_depth_best) { + best_tri_index = tri_index; + z_depth_best = z_depth; + copy_v3_v3(w, w_tmp); + } + } + } + + /** will be -1 or a valid face. */ + return best_tri_index; } /* Converts a uv coord into a pixel location wrapping if the uv is outside 0-1 range */ static void uvco_to_wrapped_pxco(const float uv[2], int ibuf_x, int ibuf_y, float *x, float *y) { - /* use */ - *x = fmodf(uv[0], 1.0f); - *y = fmodf(uv[1], 1.0f); + /* use */ + *x = fmodf(uv[0], 1.0f); + *y = fmodf(uv[1], 1.0f); - if (*x < 0.0f) *x += 1.0f; - if (*y < 0.0f) *y += 1.0f; + if (*x < 0.0f) + *x += 1.0f; + if (*y < 0.0f) + *y += 1.0f; - *x = *x * ibuf_x - 0.5f; - *y = *y * ibuf_y - 0.5f; + *x = *x * ibuf_x - 0.5f; + *y = *y * ibuf_y - 0.5f; } /* Set the top-most face color that the screen space coord 'pt' touches * (or return 0 if none touch) */ -static bool project_paint_PickColor( - const ProjPaintState *ps, const float pt[2], - float *rgba_fp, unsigned char *rgba, const bool interp) +static bool project_paint_PickColor(const ProjPaintState *ps, + const float pt[2], + float *rgba_fp, + unsigned char *rgba, + const bool interp) { - const MLoopTri *lt; - const float *lt_tri_uv[3]; - float w[3], uv[2]; - int tri_index; - Image *ima; - ImBuf *ibuf; - int xi, yi; - - tri_index = project_paint_PickFace(ps, pt, w); - - if (tri_index == -1) - return 0; - - lt = &ps->mlooptri_eval[tri_index]; - PS_LOOPTRI_ASSIGN_UV_3(lt_tri_uv, ps->poly_to_loop_uv, lt); - - interp_v2_v2v2v2(uv, UNPACK3(lt_tri_uv), w); - - ima = project_paint_face_paint_image(ps, tri_index); - /** we must have got the imbuf before getting here. */ - ibuf = BKE_image_get_first_ibuf(ima); - if (!ibuf) return 0; - - if (interp) { - float x, y; - uvco_to_wrapped_pxco(uv, ibuf->x, ibuf->y, &x, &y); - - if (ibuf->rect_float) { - if (rgba_fp) { - bilinear_interpolation_color_wrap(ibuf, NULL, rgba_fp, x, y); - } - else { - float rgba_tmp_f[4]; - bilinear_interpolation_color_wrap(ibuf, NULL, rgba_tmp_f, x, y); - premul_float_to_straight_uchar(rgba, rgba_tmp_f); - } - } - else { - if (rgba) { - bilinear_interpolation_color_wrap(ibuf, rgba, NULL, x, y); - } - else { - unsigned char rgba_tmp[4]; - bilinear_interpolation_color_wrap(ibuf, rgba_tmp, NULL, x, y); - straight_uchar_to_premul_float(rgba_fp, rgba_tmp); - } - } - } - else { - //xi = (int)((uv[0]*ibuf->x) + 0.5f); - //yi = (int)((uv[1]*ibuf->y) + 0.5f); - //if (xi < 0 || xi >= ibuf->x || yi < 0 || yi >= ibuf->y) return 0; - - /* wrap */ - xi = mod_i((int)(uv[0] * ibuf->x), ibuf->x); - yi = mod_i((int)(uv[1] * ibuf->y), ibuf->y); - - if (rgba) { - if (ibuf->rect_float) { - const float *rgba_tmp_fp = ibuf->rect_float + (xi + yi * ibuf->x * 4); - premul_float_to_straight_uchar(rgba, rgba_tmp_fp); - } - else { - *((unsigned int *)rgba) = *(unsigned int *)(((char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4)); - } - } - - if (rgba_fp) { - if (ibuf->rect_float) { - copy_v4_v4(rgba_fp, (ibuf->rect_float + ((xi + yi * ibuf->x) * 4))); - } - else { - unsigned char *tmp_ch = ((unsigned char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4); - straight_uchar_to_premul_float(rgba_fp, tmp_ch); - } - } - } - BKE_image_release_ibuf(ima, ibuf, NULL); - return 1; + const MLoopTri *lt; + const float *lt_tri_uv[3]; + float w[3], uv[2]; + int tri_index; + Image *ima; + ImBuf *ibuf; + int xi, yi; + + tri_index = project_paint_PickFace(ps, pt, w); + + if (tri_index == -1) + return 0; + + lt = &ps->mlooptri_eval[tri_index]; + PS_LOOPTRI_ASSIGN_UV_3(lt_tri_uv, ps->poly_to_loop_uv, lt); + + interp_v2_v2v2v2(uv, UNPACK3(lt_tri_uv), w); + + ima = project_paint_face_paint_image(ps, tri_index); + /** we must have got the imbuf before getting here. */ + ibuf = BKE_image_get_first_ibuf(ima); + if (!ibuf) + return 0; + + if (interp) { + float x, y; + uvco_to_wrapped_pxco(uv, ibuf->x, ibuf->y, &x, &y); + + if (ibuf->rect_float) { + if (rgba_fp) { + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_fp, x, y); + } + else { + float rgba_tmp_f[4]; + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_tmp_f, x, y); + premul_float_to_straight_uchar(rgba, rgba_tmp_f); + } + } + else { + if (rgba) { + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, x, y); + } + else { + unsigned char rgba_tmp[4]; + bilinear_interpolation_color_wrap(ibuf, rgba_tmp, NULL, x, y); + straight_uchar_to_premul_float(rgba_fp, rgba_tmp); + } + } + } + else { + //xi = (int)((uv[0]*ibuf->x) + 0.5f); + //yi = (int)((uv[1]*ibuf->y) + 0.5f); + //if (xi < 0 || xi >= ibuf->x || yi < 0 || yi >= ibuf->y) return 0; + + /* wrap */ + xi = mod_i((int)(uv[0] * ibuf->x), ibuf->x); + yi = mod_i((int)(uv[1] * ibuf->y), ibuf->y); + + if (rgba) { + if (ibuf->rect_float) { + const float *rgba_tmp_fp = ibuf->rect_float + (xi + yi * ibuf->x * 4); + premul_float_to_straight_uchar(rgba, rgba_tmp_fp); + } + else { + *((unsigned int *)rgba) = *(unsigned int *)(((char *)ibuf->rect) + + ((xi + yi * ibuf->x) * 4)); + } + } + + if (rgba_fp) { + if (ibuf->rect_float) { + copy_v4_v4(rgba_fp, (ibuf->rect_float + ((xi + yi * ibuf->x) * 4))); + } + else { + unsigned char *tmp_ch = ((unsigned char *)ibuf->rect) + ((xi + yi * ibuf->x) * 4); + straight_uchar_to_premul_float(rgba_fp, tmp_ch); + } + } + } + BKE_image_release_ibuf(ima, ibuf, NULL); + return 1; } /** @@ -807,121 +794,125 @@ static bool project_paint_PickColor( * - `1`: occluded * - `2`: occluded with `w[3]` weights set (need to know in some cases) */ -static int project_paint_occlude_ptv( - const float pt[3], - const float v1[4], const float v2[4], const float v3[4], - float w[3], const bool is_ortho) +static int project_paint_occlude_ptv(const float pt[3], + const float v1[4], + const float v2[4], + const float v3[4], + float w[3], + const bool is_ortho) { - /* if all are behind us, return false */ - if (v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2]) - return 0; - - /* do a 2D point in try intersection */ - if (!isect_point_tri_v2(pt, v1, v2, v3)) - return 0; - - - /* From here on we know there IS an intersection */ - /* if ALL of the verts are infront of us then we know it intersects ? */ - if (v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) { - return 1; - } - else { - /* we intersect? - find the exact depth at the point of intersection */ - /* Is this point is occluded by another face? */ - if (is_ortho) { - if (VecZDepthOrtho(pt, v1, v2, v3, w) < pt[2]) return 2; - } - else { - if (VecZDepthPersp(pt, v1, v2, v3, w) < pt[2]) return 2; - } - } - return -1; + /* if all are behind us, return false */ + if (v1[2] > pt[2] && v2[2] > pt[2] && v3[2] > pt[2]) + return 0; + + /* do a 2D point in try intersection */ + if (!isect_point_tri_v2(pt, v1, v2, v3)) + return 0; + + /* From here on we know there IS an intersection */ + /* if ALL of the verts are infront of us then we know it intersects ? */ + if (v1[2] < pt[2] && v2[2] < pt[2] && v3[2] < pt[2]) { + return 1; + } + else { + /* we intersect? - find the exact depth at the point of intersection */ + /* Is this point is occluded by another face? */ + if (is_ortho) { + if (VecZDepthOrtho(pt, v1, v2, v3, w) < pt[2]) + return 2; + } + else { + if (VecZDepthPersp(pt, v1, v2, v3, w) < pt[2]) + return 2; + } + } + return -1; } - -static int project_paint_occlude_ptv_clip( - const float pt[3], - const float v1[4], const float v2[4], const float v3[4], - const float v1_3d[3], const float v2_3d[3], const float v3_3d[3], - float w[3], const bool is_ortho, RegionView3D *rv3d) +static int project_paint_occlude_ptv_clip(const float pt[3], + const float v1[4], + const float v2[4], + const float v3[4], + const float v1_3d[3], + const float v2_3d[3], + const float v3_3d[3], + float w[3], + const bool is_ortho, + RegionView3D *rv3d) { - float wco[3]; - int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, is_ortho); + float wco[3]; + int ret = project_paint_occlude_ptv(pt, v1, v2, v3, w, is_ortho); - if (ret <= 0) - return ret; + if (ret <= 0) + return ret; - if (ret == 1) { /* weights not calculated */ - if (is_ortho) { - barycentric_weights_v2(v1, v2, v3, pt, w); - } - else { - barycentric_weights_v2_persp(v1, v2, v3, pt, w); - } - } + if (ret == 1) { /* weights not calculated */ + if (is_ortho) { + barycentric_weights_v2(v1, v2, v3, pt, w); + } + else { + barycentric_weights_v2_persp(v1, v2, v3, pt, w); + } + } - /* Test if we're in the clipped area, */ - interp_v3_v3v3v3(wco, v1_3d, v2_3d, v3_3d, w); + /* Test if we're in the clipped area, */ + interp_v3_v3v3v3(wco, v1_3d, v2_3d, v3_3d, w); - if (!ED_view3d_clipping_test(rv3d, wco, true)) { - return 1; - } + if (!ED_view3d_clipping_test(rv3d, wco, true)) { + return 1; + } - return -1; + return -1; } - /* Check if a screenspace location is occluded by any other faces * check, pixelScreenCo must be in screenspace, its Z-Depth only needs to be used for comparison * and doesn't need to be correct in relation to X and Y coords * (this is the case in perspective view) */ -static bool project_bucket_point_occluded( - const ProjPaintState *ps, LinkNode *bucketFace, - const int orig_face, const float pixelScreenCo[4]) +static bool project_bucket_point_occluded(const ProjPaintState *ps, + LinkNode *bucketFace, + const int orig_face, + const float pixelScreenCo[4]) { - int isect_ret; - const bool do_clip = ps->rv3d ? (ps->rv3d->rflag & RV3D_CLIPPING) != 0 : 0; - - /* we could return 0 for 1 face buckets, as long as this function assumes - * that the point its testing is only every originated from an existing face */ - - for (; bucketFace; bucketFace = bucketFace->next) { - const int tri_index = POINTER_AS_INT(bucketFace->link); - - if (orig_face != tri_index) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *vtri_ss[3] = { - ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], - ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], - }; - float w[3]; - - if (do_clip) { - const float *vtri_co[3] = { - ps->mvert_eval[ps->mloop_eval[lt->tri[0]].v].co, - ps->mvert_eval[ps->mloop_eval[lt->tri[1]].v].co, - ps->mvert_eval[ps->mloop_eval[lt->tri[2]].v].co, - }; - isect_ret = project_paint_occlude_ptv_clip( - pixelScreenCo, UNPACK3(vtri_ss), UNPACK3(vtri_co), - w, ps->is_ortho, ps->rv3d); - } - else { - isect_ret = project_paint_occlude_ptv( - pixelScreenCo, UNPACK3(vtri_ss), - w, ps->is_ortho); - } - - if (isect_ret >= 1) { - /* TODO - we may want to cache the first hit, - * it is not possible to swap the face order in the list anymore */ - return true; - } - } - } - return false; + int isect_ret; + const bool do_clip = ps->rv3d ? (ps->rv3d->rflag & RV3D_CLIPPING) != 0 : 0; + + /* we could return 0 for 1 face buckets, as long as this function assumes + * that the point its testing is only every originated from an existing face */ + + for (; bucketFace; bucketFace = bucketFace->next) { + const int tri_index = POINTER_AS_INT(bucketFace->link); + + if (orig_face != tri_index) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *vtri_ss[3] = { + ps->screenCoords[ps->mloop_eval[lt->tri[0]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[1]].v], + ps->screenCoords[ps->mloop_eval[lt->tri[2]].v], + }; + float w[3]; + + if (do_clip) { + const float *vtri_co[3] = { + ps->mvert_eval[ps->mloop_eval[lt->tri[0]].v].co, + ps->mvert_eval[ps->mloop_eval[lt->tri[1]].v].co, + ps->mvert_eval[ps->mloop_eval[lt->tri[2]].v].co, + }; + isect_ret = project_paint_occlude_ptv_clip( + pixelScreenCo, UNPACK3(vtri_ss), UNPACK3(vtri_co), w, ps->is_ortho, ps->rv3d); + } + else { + isect_ret = project_paint_occlude_ptv(pixelScreenCo, UNPACK3(vtri_ss), w, ps->is_ortho); + } + + if (isect_ret >= 1) { + /* TODO - we may want to cache the first hit, + * it is not possible to swap the face order in the list anymore */ + return true; + } + } + } + return false; } /* Basic line intersection, could move to math_geom.c, 2 points with a horizontal line @@ -931,77 +922,77 @@ static bool project_bucket_point_occluded( #define ISECT_TRUE_P2 3 static int line_isect_y(const float p1[2], const float p2[2], const float y_level, float *x_isect) { - float y_diff; - - /* are we touching the first point? - no interpolation needed */ - if (y_level == p1[1]) { - *x_isect = p1[0]; - return ISECT_TRUE_P1; - } - /* are we touching the second point? - no interpolation needed */ - if (y_level == p2[1]) { - *x_isect = p2[0]; - return ISECT_TRUE_P2; - } - - /** yuck, horizontal line, we cant do much here. */ - y_diff = fabsf(p1[1] - p2[1]); - - if (y_diff < 0.000001f) { - *x_isect = (p1[0] + p2[0]) * 0.5f; - return ISECT_TRUE; - } - - if (p1[1] > y_level && p2[1] < y_level) { - /* (p1[1] - p2[1]); */ - *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff; - return ISECT_TRUE; - } - else if (p1[1] < y_level && p2[1] > y_level) { - /* (p2[1] - p1[1]); */ - *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff; - return ISECT_TRUE; - } - else { - return 0; - } + float y_diff; + + /* are we touching the first point? - no interpolation needed */ + if (y_level == p1[1]) { + *x_isect = p1[0]; + return ISECT_TRUE_P1; + } + /* are we touching the second point? - no interpolation needed */ + if (y_level == p2[1]) { + *x_isect = p2[0]; + return ISECT_TRUE_P2; + } + + /** yuck, horizontal line, we cant do much here. */ + y_diff = fabsf(p1[1] - p2[1]); + + if (y_diff < 0.000001f) { + *x_isect = (p1[0] + p2[0]) * 0.5f; + return ISECT_TRUE; + } + + if (p1[1] > y_level && p2[1] < y_level) { + /* (p1[1] - p2[1]); */ + *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff; + return ISECT_TRUE; + } + else if (p1[1] < y_level && p2[1] > y_level) { + /* (p2[1] - p1[1]); */ + *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff; + return ISECT_TRUE; + } + else { + return 0; + } } static int line_isect_x(const float p1[2], const float p2[2], const float x_level, float *y_isect) { - float x_diff; - - if (x_level == p1[0]) { /* are we touching the first point? - no interpolation needed */ - *y_isect = p1[1]; - return ISECT_TRUE_P1; - } - if (x_level == p2[0]) { /* are we touching the second point? - no interpolation needed */ - *y_isect = p2[1]; - return ISECT_TRUE_P2; - } - - /* yuck, horizontal line, we cant do much here */ - x_diff = fabsf(p1[0] - p2[0]); - - /* yuck, vertical line, we cant do much here */ - if (x_diff < 0.000001f) { - *y_isect = (p1[0] + p2[0]) * 0.5f; - return ISECT_TRUE; - } - - if (p1[0] > x_level && p2[0] < x_level) { - /* (p1[0] - p2[0]); */ - *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff; - return ISECT_TRUE; - } - else if (p1[0] < x_level && p2[0] > x_level) { - /* (p2[0] - p1[0]); */ - *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff; - return ISECT_TRUE; - } - else { - return 0; - } + float x_diff; + + if (x_level == p1[0]) { /* are we touching the first point? - no interpolation needed */ + *y_isect = p1[1]; + return ISECT_TRUE_P1; + } + if (x_level == p2[0]) { /* are we touching the second point? - no interpolation needed */ + *y_isect = p2[1]; + return ISECT_TRUE_P2; + } + + /* yuck, horizontal line, we cant do much here */ + x_diff = fabsf(p1[0] - p2[0]); + + /* yuck, vertical line, we cant do much here */ + if (x_diff < 0.000001f) { + *y_isect = (p1[0] + p2[0]) * 0.5f; + return ISECT_TRUE; + } + + if (p1[0] > x_level && p2[0] < x_level) { + /* (p1[0] - p2[0]); */ + *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff; + return ISECT_TRUE; + } + else if (p1[0] < x_level && p2[0] > x_level) { + /* (p2[0] - p1[0]); */ + *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff; + return ISECT_TRUE; + } + else { + return 0; + } } /* simple func use for comparing UV locations to check if there are seams. @@ -1011,389 +1002,394 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve #ifndef PROJ_DEBUG_NOSEAMBLEED static bool cmp_uv(const float vec2a[2], const float vec2b[2]) { - /* if the UV's are not between 0.0 and 1.0 */ - float xa = fmodf(vec2a[0], 1.0f); - float ya = fmodf(vec2a[1], 1.0f); + /* if the UV's are not between 0.0 and 1.0 */ + float xa = fmodf(vec2a[0], 1.0f); + float ya = fmodf(vec2a[1], 1.0f); - float xb = fmodf(vec2b[0], 1.0f); - float yb = fmodf(vec2b[1], 1.0f); + float xb = fmodf(vec2b[0], 1.0f); + float yb = fmodf(vec2b[1], 1.0f); - if (xa < 0.0f) xa += 1.0f; - if (ya < 0.0f) ya += 1.0f; + if (xa < 0.0f) + xa += 1.0f; + if (ya < 0.0f) + ya += 1.0f; - if (xb < 0.0f) xb += 1.0f; - if (yb < 0.0f) yb += 1.0f; + if (xb < 0.0f) + xb += 1.0f; + if (yb < 0.0f) + yb += 1.0f; - return ((fabsf(xa - xb) < PROJ_GEOM_TOLERANCE) && (fabsf(ya - yb) < PROJ_GEOM_TOLERANCE)) ? 1 : 0; + return ((fabsf(xa - xb) < PROJ_GEOM_TOLERANCE) && (fabsf(ya - yb) < PROJ_GEOM_TOLERANCE)) ? 1 : + 0; } #endif /* set min_px and max_px to the image space bounds of the UV coords * return zero if there is no area in the returned rectangle */ #ifndef PROJ_DEBUG_NOSEAMBLEED -static bool pixel_bounds_uv( - const float uv_quad[4][2], - rcti *bounds_px, - const int ibuf_x, const int ibuf_y - ) +static bool pixel_bounds_uv(const float uv_quad[4][2], + rcti *bounds_px, + const int ibuf_x, + const int ibuf_y) { - /* UV bounds */ - float min_uv[2], max_uv[2]; + /* UV bounds */ + float min_uv[2], max_uv[2]; - INIT_MINMAX2(min_uv, max_uv); + INIT_MINMAX2(min_uv, max_uv); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[0]); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[1]); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[2]); - minmax_v2v2_v2(min_uv, max_uv, uv_quad[3]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[0]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[1]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[2]); + minmax_v2v2_v2(min_uv, max_uv, uv_quad[3]); - bounds_px->xmin = (int)(ibuf_x * min_uv[0]); - bounds_px->ymin = (int)(ibuf_y * min_uv[1]); + bounds_px->xmin = (int)(ibuf_x * min_uv[0]); + bounds_px->ymin = (int)(ibuf_y * min_uv[1]); - bounds_px->xmax = (int)(ibuf_x * max_uv[0]) + 1; - bounds_px->ymax = (int)(ibuf_y * max_uv[1]) + 1; + bounds_px->xmax = (int)(ibuf_x * max_uv[0]) + 1; + bounds_px->ymax = (int)(ibuf_y * max_uv[1]) + 1; - /*printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);*/ + /*printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);*/ - /* face uses no UV area when quantized to pixels? */ - return (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1; + /* face uses no UV area when quantized to pixels? */ + return (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1; } #endif -static bool pixel_bounds_array(float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) +static bool pixel_bounds_array( + float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) { - /* UV bounds */ - float min_uv[2], max_uv[2]; + /* UV bounds */ + float min_uv[2], max_uv[2]; - if (tot == 0) { - return 0; - } + if (tot == 0) { + return 0; + } - INIT_MINMAX2(min_uv, max_uv); + INIT_MINMAX2(min_uv, max_uv); - while (tot--) { - minmax_v2v2_v2(min_uv, max_uv, (*uv)); - uv++; - } + while (tot--) { + minmax_v2v2_v2(min_uv, max_uv, (*uv)); + uv++; + } - bounds_px->xmin = (int)(ibuf_x * min_uv[0]); - bounds_px->ymin = (int)(ibuf_y * min_uv[1]); + bounds_px->xmin = (int)(ibuf_x * min_uv[0]); + bounds_px->ymin = (int)(ibuf_y * min_uv[1]); - bounds_px->xmax = (int)(ibuf_x * max_uv[0]) + 1; - bounds_px->ymax = (int)(ibuf_y * max_uv[1]) + 1; + bounds_px->xmax = (int)(ibuf_x * max_uv[0]) + 1; + bounds_px->ymax = (int)(ibuf_y * max_uv[1]) + 1; - /*printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);*/ + /*printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]);*/ - /* face uses no UV area when quantized to pixels? */ - return (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1; + /* face uses no UV area when quantized to pixels? */ + return (bounds_px->xmin == bounds_px->xmax || bounds_px->ymin == bounds_px->ymax) ? 0 : 1; } #ifndef PROJ_DEBUG_NOSEAMBLEED static void project_face_winding_init(const ProjPaintState *ps, const int tri_index) { - /* detect the winding of faces in uv space */ - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - float winding = cross_tri_v2(lt_tri_uv[0], lt_tri_uv[1], lt_tri_uv[2]); + /* detect the winding of faces in uv space */ + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + float winding = cross_tri_v2(lt_tri_uv[0], lt_tri_uv[1], lt_tri_uv[2]); - if (winding > 0) - ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_CW; + if (winding > 0) + ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_CW; - ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_INIT; + ps->faceWindingFlags[tri_index] |= PROJ_FACE_WINDING_INIT; } /* This function returns 1 if this face has a seam along the 2 face-vert indices * 'orig_i1_fidx' and 'orig_i2_fidx' */ -static bool check_seam( - const ProjPaintState *ps, - const int orig_face, const int orig_i1_fidx, const int orig_i2_fidx, - int *other_face, int *orig_fidx) +static bool check_seam(const ProjPaintState *ps, + const int orig_face, + const int orig_i1_fidx, + const int orig_i2_fidx, + int *other_face, + int *orig_fidx) { - const MLoopTri *orig_lt = &ps->mlooptri_eval[orig_face]; - const float *orig_lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, orig_lt) }; - /* vert indices from face vert order indices */ - const unsigned int i1 = ps->mloop_eval[orig_lt->tri[orig_i1_fidx]].v; - const unsigned int i2 = ps->mloop_eval[orig_lt->tri[orig_i2_fidx]].v; - LinkNode *node; - /* index in face */ - int i1_fidx = -1, i2_fidx = -1; - - for (node = ps->vertFaces[i1]; node; node = node->next) { - const int tri_index = POINTER_AS_INT(node->link); - - if (tri_index != orig_face) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - /* could check if the 2 faces images match here, - * but then there wouldn't be a way to return the opposite face's info */ - - - /* We need to know the order of the verts in the adjacent face - * set the i1_fidx and i2_fidx to (0,1,2,3) */ - i1_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i1); - i2_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i2); - - /* Only need to check if 'i2_fidx' is valid because we know i1_fidx is the same vert on both faces */ - if (i2_fidx != -1) { - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - Image *tpage = project_paint_face_paint_image(ps, tri_index); - Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); - - BLI_assert(i1_fidx != -1); - - /* This IS an adjacent face!, now lets check if the UVs are ok */ - - /* set up the other face */ - *other_face = tri_index; - - /* we check if difference is 1 here, else we might have a case of edge 2-0 for a tri */ - *orig_fidx = (i1_fidx < i2_fidx && (i2_fidx - i1_fidx == 1)) ? i1_fidx : i2_fidx; - - /* initialize face winding if needed */ - if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) - project_face_winding_init(ps, tri_index); - - /* first test if they have the same image */ - if ((orig_tpage == tpage) && - cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && - cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) - { - /* if faces don't have the same winding in uv space, - * they are on the same side so edge is boundary */ - if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW) != - (ps->faceWindingFlags[orig_face] & PROJ_FACE_WINDING_CW)) - { - return 1; - } - - // printf("SEAM (NONE)\n"); - return 0; - - } - else { - // printf("SEAM (UV GAP)\n"); - return 1; - } - } - } - } - // printf("SEAM (NO FACE)\n"); - *other_face = -1; - return 1; + const MLoopTri *orig_lt = &ps->mlooptri_eval[orig_face]; + const float *orig_lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, orig_lt)}; + /* vert indices from face vert order indices */ + const unsigned int i1 = ps->mloop_eval[orig_lt->tri[orig_i1_fidx]].v; + const unsigned int i2 = ps->mloop_eval[orig_lt->tri[orig_i2_fidx]].v; + LinkNode *node; + /* index in face */ + int i1_fidx = -1, i2_fidx = -1; + + for (node = ps->vertFaces[i1]; node; node = node->next) { + const int tri_index = POINTER_AS_INT(node->link); + + if (tri_index != orig_face) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + /* could check if the 2 faces images match here, + * but then there wouldn't be a way to return the opposite face's info */ + + /* We need to know the order of the verts in the adjacent face + * set the i1_fidx and i2_fidx to (0,1,2,3) */ + i1_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i1); + i2_fidx = BKE_MESH_TESSTRI_VINDEX_ORDER(lt_vtri, i2); + + /* Only need to check if 'i2_fidx' is valid because we know i1_fidx is the same vert on both faces */ + if (i2_fidx != -1) { + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + Image *tpage = project_paint_face_paint_image(ps, tri_index); + Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); + + BLI_assert(i1_fidx != -1); + + /* This IS an adjacent face!, now lets check if the UVs are ok */ + + /* set up the other face */ + *other_face = tri_index; + + /* we check if difference is 1 here, else we might have a case of edge 2-0 for a tri */ + *orig_fidx = (i1_fidx < i2_fidx && (i2_fidx - i1_fidx == 1)) ? i1_fidx : i2_fidx; + + /* initialize face winding if needed */ + if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) + project_face_winding_init(ps, tri_index); + + /* first test if they have the same image */ + if ((orig_tpage == tpage) && cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && + cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) { + /* if faces don't have the same winding in uv space, + * they are on the same side so edge is boundary */ + if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW) != + (ps->faceWindingFlags[orig_face] & PROJ_FACE_WINDING_CW)) { + return 1; + } + + // printf("SEAM (NONE)\n"); + return 0; + } + else { + // printf("SEAM (UV GAP)\n"); + return 1; + } + } + } + } + // printf("SEAM (NO FACE)\n"); + *other_face = -1; + return 1; } -static VertSeam *find_adjacent_seam(const ProjPaintState *ps, uint loop_index, uint vert_index, VertSeam **r_seam) +static VertSeam *find_adjacent_seam(const ProjPaintState *ps, + uint loop_index, + uint vert_index, + VertSeam **r_seam) { - ListBase *vert_seams = &ps->vertSeams[vert_index]; - VertSeam *seam = vert_seams->first; - VertSeam *adjacent = NULL; - - while (seam->loop != loop_index) { - seam = seam->next; - } - - if (r_seam) { - *r_seam = seam; - } - - /* Circulate through the (sorted) vert seam array, in the direction of the seam normal, - * until we find the first opposing seam, matching in UV space. */ - if (seam->normal_cw) { - LISTBASE_CIRCULAR_BACKWARD_BEGIN(vert_seams, adjacent, seam) - { - if ((adjacent->normal_cw != seam->normal_cw) && - cmp_uv(adjacent->uv, seam->uv)) - { - break; - } - } - LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam); - } - else { - LISTBASE_CIRCULAR_FORWARD_BEGIN(vert_seams, adjacent, seam) - { - if ((adjacent->normal_cw != seam->normal_cw) && - cmp_uv(adjacent->uv, seam->uv)) - { - break; - } - } - LISTBASE_CIRCULAR_FORWARD_END(vert_seams, adjacent, seam); - } - - BLI_assert(adjacent); - - return adjacent; + ListBase *vert_seams = &ps->vertSeams[vert_index]; + VertSeam *seam = vert_seams->first; + VertSeam *adjacent = NULL; + + while (seam->loop != loop_index) { + seam = seam->next; + } + + if (r_seam) { + *r_seam = seam; + } + + /* Circulate through the (sorted) vert seam array, in the direction of the seam normal, + * until we find the first opposing seam, matching in UV space. */ + if (seam->normal_cw) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (vert_seams, adjacent, seam) { + if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { + break; + } + } + LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam); + } + else { + LISTBASE_CIRCULAR_FORWARD_BEGIN (vert_seams, adjacent, seam) { + if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { + break; + } + } + LISTBASE_CIRCULAR_FORWARD_END(vert_seams, adjacent, seam); + } + + BLI_assert(adjacent); + + return adjacent; } /* Computes the normal of two seams at their intersection, * and returns the angle between the seam and its normal. */ static float compute_seam_normal(VertSeam *seam, VertSeam *adj, float r_no[2]) { - const float PI_2 = M_PI * 2.0f; - float angle[2]; - float angle_rel, angle_no; + const float PI_2 = M_PI * 2.0f; + float angle[2]; + float angle_rel, angle_no; - if (seam->normal_cw) { - angle[0] = adj->angle; - angle[1] = seam->angle; - } - else { - angle[0] = seam->angle; - angle[1] = adj->angle; - } + if (seam->normal_cw) { + angle[0] = adj->angle; + angle[1] = seam->angle; + } + else { + angle[0] = seam->angle; + angle[1] = adj->angle; + } - angle_rel = angle[1] - angle[0]; + angle_rel = angle[1] - angle[0]; - if (angle_rel < 0.0f) { - angle_rel += PI_2; - } + if (angle_rel < 0.0f) { + angle_rel += PI_2; + } - angle_rel *= 0.5f; + angle_rel *= 0.5f; - angle_no = angle_rel + angle[0]; + angle_no = angle_rel + angle[0]; - if (angle_no > M_PI) { - angle_no -= PI_2; - } + if (angle_no > M_PI) { + angle_no -= PI_2; + } - r_no[0] = cosf(angle_no); - r_no[1] = sinf(angle_no); + r_no[0] = cosf(angle_no); + r_no[1] = sinf(angle_no); - return angle_rel; + return angle_rel; } /* Calculate outset UV's, this is not the same as simply scaling the UVs, * since the outset coords are a margin that keep an even distance from the original UV's, * note that the image aspect is taken into account */ -static void uv_image_outset( - const ProjPaintState *ps, float (*orig_uv)[2], float (*puv)[2], - uint tri_index, const int ibuf_x, const int ibuf_y) +static void uv_image_outset(const ProjPaintState *ps, + float (*orig_uv)[2], + float (*puv)[2], + uint tri_index, + const int ibuf_x, + const int ibuf_y) { - int fidx[2]; - uint loop_index; - uint vert[2]; - const MLoopTri *ltri = &ps->mlooptri_eval[tri_index]; + int fidx[2]; + uint loop_index; + uint vert[2]; + const MLoopTri *ltri = &ps->mlooptri_eval[tri_index]; - float ibuf_inv[2]; + float ibuf_inv[2]; - ibuf_inv[0] = 1.0f / (float)ibuf_x; - ibuf_inv[1] = 1.0f / (float)ibuf_y; + ibuf_inv[0] = 1.0f / (float)ibuf_x; + ibuf_inv[1] = 1.0f / (float)ibuf_y; - for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) { - LoopSeamData *seam_data; - float (*seam_uvs)[2]; - float ang[2]; + for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) { + LoopSeamData *seam_data; + float(*seam_uvs)[2]; + float ang[2]; - if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0])) == 0) { - continue; - } + if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0])) == 0) { + continue; + } - loop_index = ltri->tri[fidx[0]]; + loop_index = ltri->tri[fidx[0]]; - seam_data = &ps->loopSeamData[loop_index]; - seam_uvs = seam_data->seam_uvs; + seam_data = &ps->loopSeamData[loop_index]; + seam_uvs = seam_data->seam_uvs; - if (seam_uvs[0][0] != FLT_MAX) { - continue; - } + if (seam_uvs[0][0] != FLT_MAX) { + continue; + } - fidx[1] = (fidx[0] == 2) ? 0 : fidx[0] + 1; + fidx[1] = (fidx[0] == 2) ? 0 : fidx[0] + 1; - vert[0] = ps->mloop_eval[loop_index].v; - vert[1] = ps->mloop_eval[ltri->tri[fidx[1]]].v; + vert[0] = ps->mloop_eval[loop_index].v; + vert[1] = ps->mloop_eval[ltri->tri[fidx[1]]].v; - for (uint i = 0; i < 2; i++) { - VertSeam *seam; - VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam); - float no[2]; - float len_fact; - float tri_ang; + for (uint i = 0; i < 2; i++) { + VertSeam *seam; + VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam); + float no[2]; + float len_fact; + float tri_ang; - ang[i] = compute_seam_normal(seam, adj, no); - tri_ang = ang[i] - M_PI_2; + ang[i] = compute_seam_normal(seam, adj, no); + tri_ang = ang[i] - M_PI_2; - if (tri_ang > 0.0f) { - const float dist = ps->seam_bleed_px * tanf(tri_ang); - seam_data->corner_dist_sq[i] = SQUARE(dist); - } - else { - seam_data->corner_dist_sq[i] = 0.0f; - } + if (tri_ang > 0.0f) { + const float dist = ps->seam_bleed_px * tanf(tri_ang); + seam_data->corner_dist_sq[i] = SQUARE(dist); + } + else { + seam_data->corner_dist_sq[i] = 0.0f; + } - len_fact = cosf(tri_ang); - len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact); + len_fact = cosf(tri_ang); + len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact); - /* Clamp the length factor, see: T62236. */ - len_fact = MIN2(len_fact, 10.0f); + /* Clamp the length factor, see: T62236. */ + len_fact = MIN2(len_fact, 10.0f); - mul_v2_fl(no, ps->seam_bleed_px * len_fact); + mul_v2_fl(no, ps->seam_bleed_px * len_fact); - add_v2_v2v2(seam_data->seam_puvs[i], puv[fidx[i]], no); + add_v2_v2v2(seam_data->seam_puvs[i], puv[fidx[i]], no); - mul_v2_v2v2(seam_uvs[i], seam_data->seam_puvs[i], ibuf_inv); - } + mul_v2_v2v2(seam_uvs[i], seam_data->seam_puvs[i], ibuf_inv); + } - /* Handle convergent normals (can self-intersect). */ - if ((ang[0] + ang[1]) < M_PI) { - if (isect_seg_seg_v2_simple(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1])) { - float isect_co[2]; + /* Handle convergent normals (can self-intersect). */ + if ((ang[0] + ang[1]) < M_PI) { + if (isect_seg_seg_v2_simple(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1])) { + float isect_co[2]; - isect_seg_seg_v2_point(orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1], isect_co); + isect_seg_seg_v2_point( + orig_uv[fidx[0]], seam_uvs[0], orig_uv[fidx[1]], seam_uvs[1], isect_co); - copy_v2_v2(seam_uvs[0], isect_co); - copy_v2_v2(seam_uvs[1], isect_co); - } - } - - } + copy_v2_v2(seam_uvs[0], isect_co); + copy_v2_v2(seam_uvs[1], isect_co); + } + } + } } -static void insert_seam_vert_array( - const ProjPaintState *ps, MemArena *arena, const int tri_index, - const int fidx1, const int ibuf_x, const int ibuf_y) +static void insert_seam_vert_array(const ProjPaintState *ps, + MemArena *arena, + const int tri_index, + const int fidx1, + const int ibuf_x, + const int ibuf_y) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; - const int fidx[2] = {fidx1, ((fidx1 + 1) % 3)}; - float vec[2]; + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + const int fidx[2] = {fidx1, ((fidx1 + 1) % 3)}; + float vec[2]; - VertSeam *vseam = BLI_memarena_alloc(arena, sizeof(VertSeam) * 2); + VertSeam *vseam = BLI_memarena_alloc(arena, sizeof(VertSeam) * 2); - vseam->prev = NULL; - vseam->next = NULL; + vseam->prev = NULL; + vseam->next = NULL; - vseam->tri = tri_index; - vseam->loop = lt->tri[fidx[0]]; + vseam->tri = tri_index; + vseam->loop = lt->tri[fidx[0]]; - sub_v2_v2v2(vec, lt_tri_uv[fidx[1]], lt_tri_uv[fidx[0]]); - vec[0] *= ibuf_x; - vec[1] *= ibuf_y; - vseam->angle = atan2f(vec[1], vec[0]); + sub_v2_v2v2(vec, lt_tri_uv[fidx[1]], lt_tri_uv[fidx[0]]); + vec[0] *= ibuf_x; + vec[1] *= ibuf_y; + vseam->angle = atan2f(vec[1], vec[0]); - /* If face windings are not initialized, something must be wrong. */ - BLI_assert((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) != 0); - vseam->normal_cw = (ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW); + /* If face windings are not initialized, something must be wrong. */ + BLI_assert((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) != 0); + vseam->normal_cw = (ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_CW); - copy_v2_v2(vseam->uv, lt_tri_uv[fidx[0]]); + copy_v2_v2(vseam->uv, lt_tri_uv[fidx[0]]); - vseam[1] = vseam[0]; - vseam[1].angle += vseam[1].angle > 0.0f ? -M_PI : M_PI; - vseam[1].normal_cw = !vseam[1].normal_cw; - copy_v2_v2(vseam[1].uv, lt_tri_uv[fidx[1]]); + vseam[1] = vseam[0]; + vseam[1].angle += vseam[1].angle > 0.0f ? -M_PI : M_PI; + vseam[1].normal_cw = !vseam[1].normal_cw; + copy_v2_v2(vseam[1].uv, lt_tri_uv[fidx[1]]); - for (uint i = 0; i < 2; i++) { - uint vert = ps->mloop_eval[lt->tri[fidx[i]]].v; - ListBase *list = &ps->vertSeams[vert]; - VertSeam *item = list->first; + for (uint i = 0; i < 2; i++) { + uint vert = ps->mloop_eval[lt->tri[fidx[i]]].v; + ListBase *list = &ps->vertSeams[vert]; + VertSeam *item = list->first; - while (item && item->angle < vseam[i].angle) { - item = item->next; - } + while (item && item->angle < vseam[i].angle) { + item = item->next; + } - BLI_insertlinkbefore(list, item, &vseam[i]); - } + BLI_insertlinkbefore(list, item, &vseam[i]); + } } /* @@ -1402,144 +1398,151 @@ static void insert_seam_vert_array( * * If we're multithreadng, make sure threads are locked when this is called */ -static void project_face_seams_init( - const ProjPaintState *ps, MemArena *arena, const int tri_index, const uint vert_index, - bool init_all, const int ibuf_x, const int ibuf_y) +static void project_face_seams_init(const ProjPaintState *ps, + MemArena *arena, + const int tri_index, + const uint vert_index, + bool init_all, + const int ibuf_x, + const int ibuf_y) { - /* vars for the other face, we also set its flag */ - int other_face, other_fidx; - /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */ - int fidx[2] = {2, 0}; - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - LinkNode *node; - - /* initialize face winding if needed */ - if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) - project_face_winding_init(ps, tri_index); - - do { - if (init_all || - (ps->mloop_eval[lt->tri[fidx[0]]].v == vert_index) || - (ps->mloop_eval[lt->tri[fidx[1]]].v == vert_index)) - { - if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM0 << fidx[0] | PROJ_FACE_NOSEAM0 << fidx[0])) == 0) { - if (check_seam(ps, tri_index, fidx[0], fidx[1], &other_face, &other_fidx)) { - ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM0 << fidx[0]; - insert_seam_vert_array(ps, arena, tri_index, fidx[0], ibuf_x, ibuf_y); - - if (other_face != -1) { - /* Check if the other seam is already set. We don't want to insert it in the list twice. */ - if ((ps->faceSeamFlags[other_face] & (PROJ_FACE_SEAM0 << other_fidx)) == 0) { - ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM0 << other_fidx; - insert_seam_vert_array(ps, arena, other_face, other_fidx, ibuf_x, ibuf_y); - } - } - } - else { - ps->faceSeamFlags[tri_index] |= PROJ_FACE_NOSEAM0 << fidx[0]; - ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; - - if (other_face != -1) { - /* second 4 bits for disabled */ - ps->faceSeamFlags[other_face] |= PROJ_FACE_NOSEAM0 << other_fidx; - ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM_INIT0 << other_fidx; - } - } - } - } - - fidx[1] = fidx[0]; - } while (fidx[0]--); - - if (init_all) { - char checked_verts = 0; - - fidx[0] = 2; - fidx[1] = 0; - - do { - if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM_INIT0 << fidx[0])) == 0) { - for (uint i = 0; i < 2; i++) { - uint vert; - - if ((checked_verts & (1 << fidx[i])) != 0) { - continue; - } - - vert = ps->mloop_eval[lt->tri[fidx[i]]].v; - - for (node = ps->vertFaces[vert]; node; node = node->next) { - const int tri = POINTER_AS_INT(node->link); - - project_face_seams_init(ps, arena, tri, vert, false, ibuf_x, ibuf_y); - } - - checked_verts |= 1 << fidx[i]; - } - - ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; - } - - fidx[1] = fidx[0]; - } while (fidx[0]--); - } + /* vars for the other face, we also set its flag */ + int other_face, other_fidx; + /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */ + int fidx[2] = {2, 0}; + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + LinkNode *node; + + /* initialize face winding if needed */ + if ((ps->faceWindingFlags[tri_index] & PROJ_FACE_WINDING_INIT) == 0) + project_face_winding_init(ps, tri_index); + + do { + if (init_all || (ps->mloop_eval[lt->tri[fidx[0]]].v == vert_index) || + (ps->mloop_eval[lt->tri[fidx[1]]].v == vert_index)) { + if ((ps->faceSeamFlags[tri_index] & + (PROJ_FACE_SEAM0 << fidx[0] | PROJ_FACE_NOSEAM0 << fidx[0])) == 0) { + if (check_seam(ps, tri_index, fidx[0], fidx[1], &other_face, &other_fidx)) { + ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM0 << fidx[0]; + insert_seam_vert_array(ps, arena, tri_index, fidx[0], ibuf_x, ibuf_y); + + if (other_face != -1) { + /* Check if the other seam is already set. We don't want to insert it in the list twice. */ + if ((ps->faceSeamFlags[other_face] & (PROJ_FACE_SEAM0 << other_fidx)) == 0) { + ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM0 << other_fidx; + insert_seam_vert_array(ps, arena, other_face, other_fidx, ibuf_x, ibuf_y); + } + } + } + else { + ps->faceSeamFlags[tri_index] |= PROJ_FACE_NOSEAM0 << fidx[0]; + ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; + + if (other_face != -1) { + /* second 4 bits for disabled */ + ps->faceSeamFlags[other_face] |= PROJ_FACE_NOSEAM0 << other_fidx; + ps->faceSeamFlags[other_face] |= PROJ_FACE_SEAM_INIT0 << other_fidx; + } + } + } + } + + fidx[1] = fidx[0]; + } while (fidx[0]--); + + if (init_all) { + char checked_verts = 0; + + fidx[0] = 2; + fidx[1] = 0; + + do { + if ((ps->faceSeamFlags[tri_index] & (PROJ_FACE_SEAM_INIT0 << fidx[0])) == 0) { + for (uint i = 0; i < 2; i++) { + uint vert; + + if ((checked_verts & (1 << fidx[i])) != 0) { + continue; + } + + vert = ps->mloop_eval[lt->tri[fidx[i]]].v; + + for (node = ps->vertFaces[vert]; node; node = node->next) { + const int tri = POINTER_AS_INT(node->link); + + project_face_seams_init(ps, arena, tri, vert, false, ibuf_x, ibuf_y); + } + + checked_verts |= 1 << fidx[i]; + } + + ps->faceSeamFlags[tri_index] |= PROJ_FACE_SEAM_INIT0 << fidx[0]; + } + + fidx[1] = fidx[0]; + } while (fidx[0]--); + } } -#endif // PROJ_DEBUG_NOSEAMBLEED - +#endif // PROJ_DEBUG_NOSEAMBLEED /* Converts a UV location to a 3D screenspace location * Takes a 'uv' and 3 UV coords, and sets the values of pixelScreenCo * * This is used for finding a pixels location in screenspace for painting */ -static void screen_px_from_ortho( - const float uv[2], - const float v1co[3], const float v2co[3], const float v3co[3], /* Screenspace coords */ - const float uv1co[2], const float uv2co[2], const float uv3co[2], - float pixelScreenCo[4], - float w[3]) +static void screen_px_from_ortho(const float uv[2], + const float v1co[3], + const float v2co[3], + const float v3co[3], /* Screenspace coords */ + const float uv1co[2], + const float uv2co[2], + const float uv3co[2], + float pixelScreenCo[4], + float w[3]) { - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); - interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w); + barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); + interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w); } /* same as screen_px_from_ortho except we * do perspective correction on the pixel coordinate */ -static void screen_px_from_persp( - const float uv[2], - const float v1co[4], const float v2co[4], const float v3co[4], /* screenspace coords */ - const float uv1co[2], const float uv2co[2], const float uv3co[2], - float pixelScreenCo[4], - float w[3]) +static void screen_px_from_persp(const float uv[2], + const float v1co[4], + const float v2co[4], + const float v3co[4], /* screenspace coords */ + const float uv1co[2], + const float uv2co[2], + const float uv3co[2], + float pixelScreenCo[4], + float w[3]) { - float w_int[3]; - float wtot_inv, wtot; - barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); - - /* re-weight from the 4th coord of each screen vert */ - w_int[0] = w[0] * v1co[3]; - w_int[1] = w[1] * v2co[3]; - w_int[2] = w[2] * v3co[3]; - - wtot = w_int[0] + w_int[1] + w_int[2]; - - if (wtot > 0.0f) { - wtot_inv = 1.0f / wtot; - w_int[0] *= wtot_inv; - w_int[1] *= wtot_inv; - w_int[2] *= wtot_inv; - } - else { - w[0] = w[1] = w[2] = - /* dummy values for zero area face */ - w_int[0] = w_int[1] = w_int[2] = 1.0f / 3.0f; - } - /* done re-weighting */ - - /* do interpolation based on projected weight */ - interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w_int); + float w_int[3]; + float wtot_inv, wtot; + barycentric_weights_v2(uv1co, uv2co, uv3co, uv, w); + + /* re-weight from the 4th coord of each screen vert */ + w_int[0] = w[0] * v1co[3]; + w_int[1] = w[1] * v2co[3]; + w_int[2] = w[2] * v3co[3]; + + wtot = w_int[0] + w_int[1] + w_int[2]; + + if (wtot > 0.0f) { + wtot_inv = 1.0f / wtot; + w_int[0] *= wtot_inv; + w_int[1] *= wtot_inv; + w_int[2] *= wtot_inv; + } + else { + w[0] = w[1] = w[2] = + /* dummy values for zero area face */ + w_int[0] = w_int[1] = w_int[2] = 1.0f / 3.0f; + } + /* done re-weighting */ + + /* do interpolation based on projected weight */ + interp_v3_v3v3v3(pixelScreenCo, v1co, v2co, v3co, w_int); } - /** * Set a direction vector based on a screen location. * (use for perspective view, else we can simply use `ps->viewDir`) @@ -1548,16 +1551,18 @@ static void screen_px_from_persp( * * \param r_dir: Resulting direction (length is undefined). */ -static void screen_px_to_vector_persp( - int winx, int winy, const float projmat_inv[4][4], const float view_pos[3], - const float co_px[2], - float r_dir[3]) +static void screen_px_to_vector_persp(int winx, + int winy, + const float projmat_inv[4][4], + const float view_pos[3], + const float co_px[2], + float r_dir[3]) { - r_dir[0] = 2.0f * (co_px[0] / winx) - 1.0f; - r_dir[1] = 2.0f * (co_px[1] / winy) - 1.0f; - r_dir[2] = -0.5f; - mul_project_m4_v3((float(*)[4])projmat_inv, r_dir); - sub_v3_v3(r_dir, view_pos); + r_dir[0] = 2.0f * (co_px[0] / winx) - 1.0f; + r_dir[1] = 2.0f * (co_px[1] / winy) - 1.0f; + r_dir[2] = -0.5f; + mul_project_m4_v3((float(*)[4])projmat_inv, r_dir); + sub_v3_v3(r_dir, view_pos); } /** @@ -1568,578 +1573,601 @@ static void screen_px_to_vector_persp( * \param p: 2D screen-space location. * \param v1, v2: 3D object-space locations. */ -static float screen_px_line_point_factor_v2_persp( - const ProjPaintState *ps, - const float p[2], - const float v1[3], const float v2[3]) +static float screen_px_line_point_factor_v2_persp(const ProjPaintState *ps, + const float p[2], + const float v1[3], + const float v2[3]) { - const float zero[3] = {0}; - float v1_proj[3], v2_proj[3]; - float dir[3]; + const float zero[3] = {0}; + float v1_proj[3], v2_proj[3]; + float dir[3]; - screen_px_to_vector_persp(ps->winx, ps->winy, ps->projectMatInv, ps->viewPos, p, dir); + screen_px_to_vector_persp(ps->winx, ps->winy, ps->projectMatInv, ps->viewPos, p, dir); - sub_v3_v3v3(v1_proj, v1, ps->viewPos); - sub_v3_v3v3(v2_proj, v2, ps->viewPos); + sub_v3_v3v3(v1_proj, v1, ps->viewPos); + sub_v3_v3v3(v2_proj, v2, ps->viewPos); - project_plane_v3_v3v3(v1_proj, v1_proj, dir); - project_plane_v3_v3v3(v2_proj, v2_proj, dir); + project_plane_v3_v3v3(v1_proj, v1_proj, dir); + project_plane_v3_v3v3(v2_proj, v2_proj, dir); - return line_point_factor_v2(zero, v1_proj, v2_proj); + return line_point_factor_v2(zero, v1_proj, v2_proj); } - -static void project_face_pixel( - const float *lt_tri_uv[3], ImBuf *ibuf_other, const float w[3], - unsigned char rgba_ub[4], float rgba_f[4]) +static void project_face_pixel(const float *lt_tri_uv[3], + ImBuf *ibuf_other, + const float w[3], + unsigned char rgba_ub[4], + float rgba_f[4]) { - float uv_other[2], x, y; - - interp_v2_v2v2v2(uv_other, UNPACK3(lt_tri_uv), w); + float uv_other[2], x, y; - /* use */ - uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y); + interp_v2_v2v2v2(uv_other, UNPACK3(lt_tri_uv), w); - if (ibuf_other->rect_float) { /* from float to float */ - bilinear_interpolation_color_wrap(ibuf_other, NULL, rgba_f, x, y); - } - else { /* from char to float */ - bilinear_interpolation_color_wrap(ibuf_other, rgba_ub, NULL, x, y); - } + /* use */ + uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y); + if (ibuf_other->rect_float) { /* from float to float */ + bilinear_interpolation_color_wrap(ibuf_other, NULL, rgba_f, x, y); + } + else { /* from char to float */ + bilinear_interpolation_color_wrap(ibuf_other, rgba_ub, NULL, x, y); + } } /* run this outside project_paint_uvpixel_init since pixels with mask 0 don't need init */ -static float project_paint_uvpixel_mask( - const ProjPaintState *ps, - const int tri_index, - const float w[3]) +static float project_paint_uvpixel_mask(const ProjPaintState *ps, + const int tri_index, + const float w[3]) { - float mask; - - /* Image Mask */ - if (ps->do_layer_stencil) { - /* another UV maps image is masking this one's */ - ImBuf *ibuf_other; - Image *other_tpage = ps->stencil_ima; - - if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { - const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; - const float *lt_other_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other) }; - - /* BKE_image_acquire_ibuf - TODO - this may be slow */ - unsigned char rgba_ub[4]; - float rgba_f[4]; - - project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, rgba_f); - - if (ibuf_other->rect_float) { /* from float to float */ - mask = ((rgba_f[0] + rgba_f[1] + rgba_f[2]) * (1.0f / 3.0f)) * rgba_f[3]; - } - else { /* from char to float */ - mask = ((rgba_ub[0] + rgba_ub[1] + rgba_ub[2]) * (1.0f / (255.0f * 3.0f))) * (rgba_ub[3] * (1.0f / 255.0f)); - } - - BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); - - if (!ps->do_layer_stencil_inv) { - /* matching the gimps layer mask black/white rules, white==full opacity */ - mask = (1.0f - mask); - } - - if (mask == 0.0f) { - return 0.0f; - } - } - else { - return 0.0f; - } - } - else { - mask = 1.0f; - } - - if (ps->do_mask_cavity) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - float ca1, ca2, ca3, ca_mask; - ca1 = ps->cavities[lt_vtri[0]]; - ca2 = ps->cavities[lt_vtri[1]]; - ca3 = ps->cavities[lt_vtri[2]]; - - ca_mask = w[0] * ca1 + w[1] * ca2 + w[2] * ca3; - ca_mask = curvemapping_evaluateF(ps->cavity_curve, 0, ca_mask); - CLAMP(ca_mask, 0.0f, 1.0f); - mask *= ca_mask; - } - - /* calculate mask */ - if (ps->do_mask_normal) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - const MPoly *mp = &ps->mpoly_eval[lt->poly]; - float no[3], angle_cos; - - if (mp->flag & ME_SMOOTH) { - const short *no1, *no2, *no3; - no1 = ps->mvert_eval[lt_vtri[0]].no; - no2 = ps->mvert_eval[lt_vtri[1]].no; - no3 = ps->mvert_eval[lt_vtri[2]].no; - - no[0] = w[0] * no1[0] + w[1] * no2[0] + w[2] * no3[0]; - no[1] = w[0] * no1[1] + w[1] * no2[1] + w[2] * no3[1]; - no[2] = w[0] * no1[2] + w[1] * no2[2] + w[2] * no3[2]; - normalize_v3(no); - } - else { - /* incase the */ + float mask; + + /* Image Mask */ + if (ps->do_layer_stencil) { + /* another UV maps image is masking this one's */ + ImBuf *ibuf_other; + Image *other_tpage = ps->stencil_ima; + + if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { + const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; + const float *lt_other_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other)}; + + /* BKE_image_acquire_ibuf - TODO - this may be slow */ + unsigned char rgba_ub[4]; + float rgba_f[4]; + + project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, rgba_f); + + if (ibuf_other->rect_float) { /* from float to float */ + mask = ((rgba_f[0] + rgba_f[1] + rgba_f[2]) * (1.0f / 3.0f)) * rgba_f[3]; + } + else { /* from char to float */ + mask = ((rgba_ub[0] + rgba_ub[1] + rgba_ub[2]) * (1.0f / (255.0f * 3.0f))) * + (rgba_ub[3] * (1.0f / 255.0f)); + } + + BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); + + if (!ps->do_layer_stencil_inv) { + /* matching the gimps layer mask black/white rules, white==full opacity */ + mask = (1.0f - mask); + } + + if (mask == 0.0f) { + return 0.0f; + } + } + else { + return 0.0f; + } + } + else { + mask = 1.0f; + } + + if (ps->do_mask_cavity) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + float ca1, ca2, ca3, ca_mask; + ca1 = ps->cavities[lt_vtri[0]]; + ca2 = ps->cavities[lt_vtri[1]]; + ca3 = ps->cavities[lt_vtri[2]]; + + ca_mask = w[0] * ca1 + w[1] * ca2 + w[2] * ca3; + ca_mask = curvemapping_evaluateF(ps->cavity_curve, 0, ca_mask); + CLAMP(ca_mask, 0.0f, 1.0f); + mask *= ca_mask; + } + + /* calculate mask */ + if (ps->do_mask_normal) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + const MPoly *mp = &ps->mpoly_eval[lt->poly]; + float no[3], angle_cos; + + if (mp->flag & ME_SMOOTH) { + const short *no1, *no2, *no3; + no1 = ps->mvert_eval[lt_vtri[0]].no; + no2 = ps->mvert_eval[lt_vtri[1]].no; + no3 = ps->mvert_eval[lt_vtri[2]].no; + + no[0] = w[0] * no1[0] + w[1] * no2[0] + w[2] * no3[0]; + no[1] = w[0] * no1[1] + w[1] * no2[1] + w[2] * no3[1]; + no[2] = w[0] * no1[2] + w[1] * no2[2] + w[2] * no3[2]; + normalize_v3(no); + } + else { + /* incase the */ #if 1 - /* normalizing per pixel isn't optimal, we could cache or check ps->*/ - normal_tri_v3(no, - ps->mvert_eval[lt_vtri[0]].co, - ps->mvert_eval[lt_vtri[1]].co, - ps->mvert_eval[lt_vtri[2]].co); + /* normalizing per pixel isn't optimal, we could cache or check ps->*/ + normal_tri_v3(no, + ps->mvert_eval[lt_vtri[0]].co, + ps->mvert_eval[lt_vtri[1]].co, + ps->mvert_eval[lt_vtri[2]].co); #else - /* don't use because some modifiers dont have normal data (subsurf for eg) */ - copy_v3_v3(no, (float *)ps->dm->getTessFaceData(ps->dm, tri_index, CD_NORMAL)); + /* don't use because some modifiers dont have normal data (subsurf for eg) */ + copy_v3_v3(no, (float *)ps->dm->getTessFaceData(ps->dm, tri_index, CD_NORMAL)); #endif - } - - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(no); - } - - /* now we can use the normal as a mask */ - if (ps->is_ortho) { - angle_cos = dot_v3v3(ps->viewDir, no); - } - else { - /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */ - float viewDirPersp[3]; - const float *co1, *co2, *co3; - co1 = ps->mvert_eval[lt_vtri[0]].co; - co2 = ps->mvert_eval[lt_vtri[1]].co; - co3 = ps->mvert_eval[lt_vtri[2]].co; - - /* Get the direction from the viewPoint to the pixel and normalize */ - viewDirPersp[0] = (ps->viewPos[0] - (w[0] * co1[0] + w[1] * co2[0] + w[2] * co3[0])); - viewDirPersp[1] = (ps->viewPos[1] - (w[0] * co1[1] + w[1] * co2[1] + w[2] * co3[1])); - viewDirPersp[2] = (ps->viewPos[2] - (w[0] * co1[2] + w[1] * co2[2] + w[2] * co3[2])); - normalize_v3(viewDirPersp); - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(viewDirPersp); - } - - angle_cos = dot_v3v3(viewDirPersp, no); - } - - /* If backface culling is disabled, allow painting on back faces. */ - if (!ps->do_backfacecull) { - angle_cos = fabsf(angle_cos); - } - - if (angle_cos <= ps->normal_angle__cos) { - /* outsize the normal limit*/ - return 0.0f; - } - else if (angle_cos < ps->normal_angle_inner__cos) { - mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range; - } /* otherwise no mask normal is needed, were within the limit */ - } - - /* This only works when the opacity doesn't change while painting, stylus pressure messes with this - * so don't use it. */ - // if (ps->is_airbrush == 0) mask *= BKE_brush_alpha_get(ps->brush); - - return mask; + } + + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } + + /* now we can use the normal as a mask */ + if (ps->is_ortho) { + angle_cos = dot_v3v3(ps->viewDir, no); + } + else { + /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */ + float viewDirPersp[3]; + const float *co1, *co2, *co3; + co1 = ps->mvert_eval[lt_vtri[0]].co; + co2 = ps->mvert_eval[lt_vtri[1]].co; + co3 = ps->mvert_eval[lt_vtri[2]].co; + + /* Get the direction from the viewPoint to the pixel and normalize */ + viewDirPersp[0] = (ps->viewPos[0] - (w[0] * co1[0] + w[1] * co2[0] + w[2] * co3[0])); + viewDirPersp[1] = (ps->viewPos[1] - (w[0] * co1[1] + w[1] * co2[1] + w[2] * co3[1])); + viewDirPersp[2] = (ps->viewPos[2] - (w[0] * co1[2] + w[1] * co2[2] + w[2] * co3[2])); + normalize_v3(viewDirPersp); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(viewDirPersp); + } + + angle_cos = dot_v3v3(viewDirPersp, no); + } + + /* If backface culling is disabled, allow painting on back faces. */ + if (!ps->do_backfacecull) { + angle_cos = fabsf(angle_cos); + } + + if (angle_cos <= ps->normal_angle__cos) { + /* outsize the normal limit*/ + return 0.0f; + } + else if (angle_cos < ps->normal_angle_inner__cos) { + mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range; + } /* otherwise no mask normal is needed, were within the limit */ + } + + /* This only works when the opacity doesn't change while painting, stylus pressure messes with this + * so don't use it. */ + // if (ps->is_airbrush == 0) mask *= BKE_brush_alpha_get(ps->brush); + + return mask; } static int project_paint_pixel_sizeof(const short tool) { - if ((tool == PAINT_TOOL_CLONE) || (tool == PAINT_TOOL_SMEAR)) { - return sizeof(ProjPixelClone); - } - else { - return sizeof(ProjPixel); - } + if ((tool == PAINT_TOOL_CLONE) || (tool == PAINT_TOOL_SMEAR)) { + return sizeof(ProjPixelClone); + } + else { + return sizeof(ProjPixel); + } } static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) { - ProjPaintImage *pjIma = tinf->pjima; - int tile_index = tx + ty * tinf->tile_width; - bool generate_tile = false; - - /* double check lock to avoid locking */ - if (UNLIKELY(!pjIma->undoRect[tile_index])) { - if (tinf->lock) - BLI_spin_lock(tinf->lock); - if (LIKELY(!pjIma->undoRect[tile_index])) { - pjIma->undoRect[tile_index] = TILE_PENDING; - generate_tile = true; - } - if (tinf->lock) - BLI_spin_unlock(tinf->lock); - } - - - if (generate_tile) { - ListBase *undo_tiles = ED_image_undo_get_tiles(); - volatile void *undorect; - if (tinf->masked) { - undorect = image_undo_push_tile( - undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf, - tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false); - } - else { - undorect = image_undo_push_tile( - undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf, - tx, ty, NULL, &pjIma->valid[tile_index], true, false); - } - - pjIma->ibuf->userflags |= IB_BITMAPDIRTY; - /* tile ready, publish */ - if (tinf->lock) - BLI_spin_lock(tinf->lock); - pjIma->undoRect[tile_index] = undorect; - if (tinf->lock) - BLI_spin_unlock(tinf->lock); - - } - - return tile_index; + ProjPaintImage *pjIma = tinf->pjima; + int tile_index = tx + ty * tinf->tile_width; + bool generate_tile = false; + + /* double check lock to avoid locking */ + if (UNLIKELY(!pjIma->undoRect[tile_index])) { + if (tinf->lock) + BLI_spin_lock(tinf->lock); + if (LIKELY(!pjIma->undoRect[tile_index])) { + pjIma->undoRect[tile_index] = TILE_PENDING; + generate_tile = true; + } + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + } + + if (generate_tile) { + ListBase *undo_tiles = ED_image_undo_get_tiles(); + volatile void *undorect; + if (tinf->masked) { + undorect = image_undo_push_tile(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + &pjIma->maskRect[tile_index], + &pjIma->valid[tile_index], + true, + false); + } + else { + undorect = image_undo_push_tile(undo_tiles, + pjIma->ima, + pjIma->ibuf, + tinf->tmpibuf, + tx, + ty, + NULL, + &pjIma->valid[tile_index], + true, + false); + } + + pjIma->ibuf->userflags |= IB_BITMAPDIRTY; + /* tile ready, publish */ + if (tinf->lock) + BLI_spin_lock(tinf->lock); + pjIma->undoRect[tile_index] = undorect; + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + } + + return tile_index; } /* run this function when we know a bucket's, face's pixel can be initialized, * return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */ -static ProjPixel *project_paint_uvpixel_init( - const ProjPaintState *ps, - MemArena *arena, - const TileInfo *tinf, - int x_px, int y_px, - const float mask, - const int tri_index, - const float pixelScreenCo[4], - const float world_spaceCo[3], - const float w[3]) +static ProjPixel *project_paint_uvpixel_init(const ProjPaintState *ps, + MemArena *arena, + const TileInfo *tinf, + int x_px, + int y_px, + const float mask, + const int tri_index, + const float pixelScreenCo[4], + const float world_spaceCo[3], + const float w[3]) { - ProjPixel *projPixel; - int x_tile, y_tile; - int x_round, y_round; - int tile_offset; - /* volatile is important here to ensure pending check is not optimized away by compiler*/ - volatile int tile_index; - - ProjPaintImage *projima = tinf->pjima; - ImBuf *ibuf = projima->ibuf; - /* wrap pixel location */ - - x_px = mod_i(x_px, ibuf->x); - y_px = mod_i(y_px, ibuf->y); - - BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); - projPixel = BLI_memarena_alloc(arena, ps->pixel_sizeof); - - /* calculate the undo tile offset of the pixel, used to store the original - * pixel color and accumulated mask if any */ - x_tile = x_px >> IMAPAINT_TILE_BITS; - y_tile = y_px >> IMAPAINT_TILE_BITS; - - x_round = x_tile * IMAPAINT_TILE_SIZE; - y_round = y_tile * IMAPAINT_TILE_SIZE; - //memset(projPixel, 0, size); - - tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; - tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); - - /* other thread may be initializing the tile so wait here */ - while (projima->undoRect[tile_index] == TILE_PENDING) - ; - - BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); - BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); - - projPixel->valid = projima->valid[tile_index]; - - if (ibuf->rect_float) { - projPixel->pixel.f_pt = ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4); - projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset; - zero_v4(projPixel->newColor.f); - } - else { - projPixel->pixel.ch_pt = (unsigned char *)(ibuf->rect + (x_px + y_px * ibuf->x)); - projPixel->origColor.uint_pt = (unsigned int *)projima->undoRect[tile_index] + tile_offset; - projPixel->newColor.uint = 0; - } - - /* screenspace unclamped, we could keep its z and w values but don't need them at the moment */ - if (ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - copy_v3_v3(projPixel->worldCoSS, world_spaceCo); - } - - copy_v2_v2(projPixel->projCoSS, pixelScreenCo); - - projPixel->x_px = x_px; - projPixel->y_px = y_px; - - projPixel->mask = (unsigned short)(mask * 65535); - if (ps->do_masking) - projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset; - else - projPixel->mask_accum = NULL; - - /* which bounding box cell are we in?, needed for undo */ - projPixel->bb_cell_index = ((int)(((float)x_px / (float)ibuf->x) * PROJ_BOUNDBOX_DIV)) + - ((int)(((float)y_px / (float)ibuf->y) * PROJ_BOUNDBOX_DIV)) * PROJ_BOUNDBOX_DIV; - - /* done with view3d_project_float inline */ - if (ps->tool == PAINT_TOOL_CLONE) { - if (ps->poly_to_loop_uv_clone) { - ImBuf *ibuf_other; - Image *other_tpage = project_paint_face_clone_image(ps, tri_index); - - if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { - const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; - const float *lt_other_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv_clone, lt_other) }; - - /* BKE_image_acquire_ibuf - TODO - this may be slow */ - - if (ibuf->rect_float) { - if (ibuf_other->rect_float) { /* from float to float */ - project_face_pixel(lt_other_tri_uv, ibuf_other, w, NULL, ((ProjPixelClone *)projPixel)->clonepx.f); - } - else { /* from char to float */ - unsigned char rgba_ub[4]; - float rgba[4]; - project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, NULL); - if (ps->use_colormanagement) { - srgb_to_linearrgb_uchar4(rgba, rgba_ub); - } - else { - rgba_uchar_to_float(rgba, rgba_ub); - } - straight_to_premul_v4_v4(((ProjPixelClone *)projPixel)->clonepx.f, rgba); - } - } - else { - if (ibuf_other->rect_float) { /* float to char */ - float rgba[4]; - project_face_pixel(lt_other_tri_uv, ibuf_other, w, NULL, rgba); - premul_to_straight_v4(rgba); - if (ps->use_colormanagement) { - linearrgb_to_srgb_uchar3(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); - } - else { - rgb_float_to_uchar(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); - } - ((ProjPixelClone *)projPixel)->clonepx.ch[3] = rgba[3] * 255; - } - else { /* char to char */ - project_face_pixel(lt_other_tri_uv, ibuf_other, w, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL); - } - } - - BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); - } - else { - if (ibuf->rect_float) { - ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; - } - else { - ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; - } - } - - } - else { - float co[2]; - sub_v2_v2v2(co, projPixel->projCoSS, ps->cloneOffset); - - /* no need to initialize the bucket, we're only checking buckets faces and for this - * the faces are already initialized in project_paint_delayed_face_init(...) */ - if (ibuf->rect_float) { - if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) { - /* zero alpha - ignore */ - ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; - } - } - else { - if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) { - /* zero alpha - ignore */ - ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; - } - } - } - } + ProjPixel *projPixel; + int x_tile, y_tile; + int x_round, y_round; + int tile_offset; + /* volatile is important here to ensure pending check is not optimized away by compiler*/ + volatile int tile_index; + + ProjPaintImage *projima = tinf->pjima; + ImBuf *ibuf = projima->ibuf; + /* wrap pixel location */ + + x_px = mod_i(x_px, ibuf->x); + y_px = mod_i(y_px, ibuf->y); + + BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); + projPixel = BLI_memarena_alloc(arena, ps->pixel_sizeof); + + /* calculate the undo tile offset of the pixel, used to store the original + * pixel color and accumulated mask if any */ + x_tile = x_px >> IMAPAINT_TILE_BITS; + y_tile = y_px >> IMAPAINT_TILE_BITS; + + x_round = x_tile * IMAPAINT_TILE_SIZE; + y_round = y_tile * IMAPAINT_TILE_SIZE; + //memset(projPixel, 0, size); + + tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; + tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); + + /* other thread may be initializing the tile so wait here */ + while (projima->undoRect[tile_index] == TILE_PENDING) + ; + + BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); + BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); + + projPixel->valid = projima->valid[tile_index]; + + if (ibuf->rect_float) { + projPixel->pixel.f_pt = ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4); + projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset; + zero_v4(projPixel->newColor.f); + } + else { + projPixel->pixel.ch_pt = (unsigned char *)(ibuf->rect + (x_px + y_px * ibuf->x)); + projPixel->origColor.uint_pt = (unsigned int *)projima->undoRect[tile_index] + tile_offset; + projPixel->newColor.uint = 0; + } + + /* screenspace unclamped, we could keep its z and w values but don't need them at the moment */ + if (ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { + copy_v3_v3(projPixel->worldCoSS, world_spaceCo); + } + + copy_v2_v2(projPixel->projCoSS, pixelScreenCo); + + projPixel->x_px = x_px; + projPixel->y_px = y_px; + + projPixel->mask = (unsigned short)(mask * 65535); + if (ps->do_masking) + projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset; + else + projPixel->mask_accum = NULL; + + /* which bounding box cell are we in?, needed for undo */ + projPixel->bb_cell_index = ((int)(((float)x_px / (float)ibuf->x) * PROJ_BOUNDBOX_DIV)) + + ((int)(((float)y_px / (float)ibuf->y) * PROJ_BOUNDBOX_DIV)) * + PROJ_BOUNDBOX_DIV; + + /* done with view3d_project_float inline */ + if (ps->tool == PAINT_TOOL_CLONE) { + if (ps->poly_to_loop_uv_clone) { + ImBuf *ibuf_other; + Image *other_tpage = project_paint_face_clone_image(ps, tri_index); + + if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { + const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; + const float *lt_other_tri_uv[3] = { + PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv_clone, lt_other)}; + + /* BKE_image_acquire_ibuf - TODO - this may be slow */ + + if (ibuf->rect_float) { + if (ibuf_other->rect_float) { /* from float to float */ + project_face_pixel( + lt_other_tri_uv, ibuf_other, w, NULL, ((ProjPixelClone *)projPixel)->clonepx.f); + } + else { /* from char to float */ + unsigned char rgba_ub[4]; + float rgba[4]; + project_face_pixel(lt_other_tri_uv, ibuf_other, w, rgba_ub, NULL); + if (ps->use_colormanagement) { + srgb_to_linearrgb_uchar4(rgba, rgba_ub); + } + else { + rgba_uchar_to_float(rgba, rgba_ub); + } + straight_to_premul_v4_v4(((ProjPixelClone *)projPixel)->clonepx.f, rgba); + } + } + else { + if (ibuf_other->rect_float) { /* float to char */ + float rgba[4]; + project_face_pixel(lt_other_tri_uv, ibuf_other, w, NULL, rgba); + premul_to_straight_v4(rgba); + if (ps->use_colormanagement) { + linearrgb_to_srgb_uchar3(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); + } + else { + rgb_float_to_uchar(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); + } + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = rgba[3] * 255; + } + else { /* char to char */ + project_face_pixel( + lt_other_tri_uv, ibuf_other, w, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL); + } + } + + BKE_image_release_ibuf(other_tpage, ibuf_other, NULL); + } + else { + if (ibuf->rect_float) { + ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; + } + else { + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; + } + } + } + else { + float co[2]; + sub_v2_v2v2(co, projPixel->projCoSS, ps->cloneOffset); + + /* no need to initialize the bucket, we're only checking buckets faces and for this + * the faces are already initialized in project_paint_delayed_face_init(...) */ + if (ibuf->rect_float) { + if (!project_paint_PickColor(ps, co, ((ProjPixelClone *)projPixel)->clonepx.f, NULL, 1)) { + /* zero alpha - ignore */ + ((ProjPixelClone *)projPixel)->clonepx.f[3] = 0; + } + } + else { + if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) { + /* zero alpha - ignore */ + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; + } + } + } + } #ifdef PROJ_DEBUG_PAINT - if (ibuf->rect_float) projPixel->pixel.f_pt[0] = 0; - else projPixel->pixel.ch_pt[0] = 0; + if (ibuf->rect_float) + projPixel->pixel.f_pt[0] = 0; + else + projPixel->pixel.ch_pt[0] = 0; #endif - /* pointer arithmetic */ - projPixel->image_index = projima - ps->projImages; + /* pointer arithmetic */ + projPixel->image_index = projima - ps->projImages; - return projPixel; + return projPixel; } -static bool line_clip_rect2f( - const rctf *cliprect, - const rctf *rect, - const float l1[2], const float l2[2], - float l1_clip[2], float l2_clip[2]) +static bool line_clip_rect2f(const rctf *cliprect, + const rctf *rect, + const float l1[2], + const float l2[2], + float l1_clip[2], + float l2_clip[2]) { - /* first account for horizontal, then vertical lines */ - /* horiz */ - if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { - /* is the line out of range on its Y axis? */ - if (l1[1] < rect->ymin || l1[1] > rect->ymax) { - return 0; - } - /* line is out of range on its X axis */ - if ((l1[0] < rect->xmin && l2[0] < rect->xmin) || (l1[0] > rect->xmax && l2[0] > rect->xmax)) { - return 0; - } - - - /* this is a single point (or close to)*/ - if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { - if (BLI_rctf_isect_pt_v(rect, l1)) { - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - return 1; - } - else { - return 0; - } - } - - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - CLAMP(l1_clip[0], rect->xmin, rect->xmax); - CLAMP(l2_clip[0], rect->xmin, rect->xmax); - return 1; - } - else if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { - /* is the line out of range on its X axis? */ - if (l1[0] < rect->xmin || l1[0] > rect->xmax) { - return 0; - } - - /* line is out of range on its Y axis */ - if ((l1[1] < rect->ymin && l2[1] < rect->ymin) || (l1[1] > rect->ymax && l2[1] > rect->ymax)) { - return 0; - } - - /* this is a single point (or close to)*/ - if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { - if (BLI_rctf_isect_pt_v(rect, l1)) { - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - return 1; - } - else { - return 0; - } - } - - copy_v2_v2(l1_clip, l1); - copy_v2_v2(l2_clip, l2); - CLAMP(l1_clip[1], rect->ymin, rect->ymax); - CLAMP(l2_clip[1], rect->ymin, rect->ymax); - return 1; - } - else { - float isect; - short ok1 = 0; - short ok2 = 0; - - /* Done with vertical lines */ - - /* are either of the points inside the rectangle ? */ - if (BLI_rctf_isect_pt_v(rect, l1)) { - copy_v2_v2(l1_clip, l1); - ok1 = 1; - } - - if (BLI_rctf_isect_pt_v(rect, l2)) { - copy_v2_v2(l2_clip, l2); - ok2 = 1; - } - - /* line inside rect */ - if (ok1 && ok2) return 1; - - /* top/bottom */ - if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= cliprect->xmin) && (isect <= cliprect->xmax)) { - if (l1[1] < l2[1]) { /* line 1 is outside */ - l1_clip[0] = isect; - l1_clip[1] = rect->ymin; - ok1 = 1; - } - else { - l2_clip[0] = isect; - l2_clip[1] = rect->ymin; - ok2 = 2; - } - } - - if (ok1 && ok2) return 1; - - if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= cliprect->xmin) && (isect <= cliprect->xmax)) { - if (l1[1] > l2[1]) { /* line 1 is outside */ - l1_clip[0] = isect; - l1_clip[1] = rect->ymax; - ok1 = 1; - } - else { - l2_clip[0] = isect; - l2_clip[1] = rect->ymax; - ok2 = 2; - } - } - - if (ok1 && ok2) return 1; - - /* left/right */ - if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= cliprect->ymin) && (isect <= cliprect->ymax)) { - if (l1[0] < l2[0]) { /* line 1 is outside */ - l1_clip[0] = rect->xmin; - l1_clip[1] = isect; - ok1 = 1; - } - else { - l2_clip[0] = rect->xmin; - l2_clip[1] = isect; - ok2 = 2; - } - } - - if (ok1 && ok2) return 1; - - if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= cliprect->ymin) && (isect <= cliprect->ymax)) { - if (l1[0] > l2[0]) { /* line 1 is outside */ - l1_clip[0] = rect->xmax; - l1_clip[1] = isect; - ok1 = 1; - } - else { - l2_clip[0] = rect->xmax; - l2_clip[1] = isect; - ok2 = 2; - } - } - - if (ok1 && ok2) { - return 1; - } - else { - return 0; - } - } + /* first account for horizontal, then vertical lines */ + /* horiz */ + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { + /* is the line out of range on its Y axis? */ + if (l1[1] < rect->ymin || l1[1] > rect->ymax) { + return 0; + } + /* line is out of range on its X axis */ + if ((l1[0] < rect->xmin && l2[0] < rect->xmin) || (l1[0] > rect->xmax && l2[0] > rect->xmax)) { + return 0; + } + + /* this is a single point (or close to)*/ + if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { + if (BLI_rctf_isect_pt_v(rect, l1)) { + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + return 1; + } + else { + return 0; + } + } + + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + CLAMP(l1_clip[0], rect->xmin, rect->xmax); + CLAMP(l2_clip[0], rect->xmin, rect->xmax); + return 1; + } + else if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { + /* is the line out of range on its X axis? */ + if (l1[0] < rect->xmin || l1[0] > rect->xmax) { + return 0; + } + + /* line is out of range on its Y axis */ + if ((l1[1] < rect->ymin && l2[1] < rect->ymin) || (l1[1] > rect->ymax && l2[1] > rect->ymax)) { + return 0; + } + + /* this is a single point (or close to)*/ + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { + if (BLI_rctf_isect_pt_v(rect, l1)) { + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + return 1; + } + else { + return 0; + } + } + + copy_v2_v2(l1_clip, l1); + copy_v2_v2(l2_clip, l2); + CLAMP(l1_clip[1], rect->ymin, rect->ymax); + CLAMP(l2_clip[1], rect->ymin, rect->ymax); + return 1; + } + else { + float isect; + short ok1 = 0; + short ok2 = 0; + + /* Done with vertical lines */ + + /* are either of the points inside the rectangle ? */ + if (BLI_rctf_isect_pt_v(rect, l1)) { + copy_v2_v2(l1_clip, l1); + ok1 = 1; + } + + if (BLI_rctf_isect_pt_v(rect, l2)) { + copy_v2_v2(l2_clip, l2); + ok2 = 1; + } + + /* line inside rect */ + if (ok1 && ok2) + return 1; + + /* top/bottom */ + if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= cliprect->xmin) && + (isect <= cliprect->xmax)) { + if (l1[1] < l2[1]) { /* line 1 is outside */ + l1_clip[0] = isect; + l1_clip[1] = rect->ymin; + ok1 = 1; + } + else { + l2_clip[0] = isect; + l2_clip[1] = rect->ymin; + ok2 = 2; + } + } + + if (ok1 && ok2) + return 1; + + if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= cliprect->xmin) && + (isect <= cliprect->xmax)) { + if (l1[1] > l2[1]) { /* line 1 is outside */ + l1_clip[0] = isect; + l1_clip[1] = rect->ymax; + ok1 = 1; + } + else { + l2_clip[0] = isect; + l2_clip[1] = rect->ymax; + ok2 = 2; + } + } + + if (ok1 && ok2) + return 1; + + /* left/right */ + if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= cliprect->ymin) && + (isect <= cliprect->ymax)) { + if (l1[0] < l2[0]) { /* line 1 is outside */ + l1_clip[0] = rect->xmin; + l1_clip[1] = isect; + ok1 = 1; + } + else { + l2_clip[0] = rect->xmin; + l2_clip[1] = isect; + ok2 = 2; + } + } + + if (ok1 && ok2) + return 1; + + if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= cliprect->ymin) && + (isect <= cliprect->ymax)) { + if (l1[0] > l2[0]) { /* line 1 is outside */ + l1_clip[0] = rect->xmax; + l1_clip[1] = isect; + ok1 = 1; + } + else { + l2_clip[0] = rect->xmax; + l2_clip[1] = isect; + ok2 = 2; + } + } + + if (ok1 && ok2) { + return 1; + } + else { + return 0; + } + } } - - /** * Scale the tri about its center * scaling by #PROJ_FACE_SCALE_SEAM (0.99x) is used for getting fake UV pixel coords that are on the @@ -2149,82 +2177,93 @@ static bool line_clip_rect2f( static void scale_tri(float insetCos[3][3], const float *origCos[4], const float inset) { - float cent[3]; - cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0]) * (1.0f / 3.0f); - cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1]) * (1.0f / 3.0f); - cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2]) * (1.0f / 3.0f); - - sub_v3_v3v3(insetCos[0], origCos[0], cent); - sub_v3_v3v3(insetCos[1], origCos[1], cent); - sub_v3_v3v3(insetCos[2], origCos[2], cent); - - mul_v3_fl(insetCos[0], inset); - mul_v3_fl(insetCos[1], inset); - mul_v3_fl(insetCos[2], inset); - - add_v3_v3(insetCos[0], cent); - add_v3_v3(insetCos[1], cent); - add_v3_v3(insetCos[2], cent); + float cent[3]; + cent[0] = (origCos[0][0] + origCos[1][0] + origCos[2][0]) * (1.0f / 3.0f); + cent[1] = (origCos[0][1] + origCos[1][1] + origCos[2][1]) * (1.0f / 3.0f); + cent[2] = (origCos[0][2] + origCos[1][2] + origCos[2][2]) * (1.0f / 3.0f); + + sub_v3_v3v3(insetCos[0], origCos[0], cent); + sub_v3_v3v3(insetCos[1], origCos[1], cent); + sub_v3_v3v3(insetCos[2], origCos[2], cent); + + mul_v3_fl(insetCos[0], inset); + mul_v3_fl(insetCos[1], inset); + mul_v3_fl(insetCos[2], inset); + + add_v3_v3(insetCos[0], cent); + add_v3_v3(insetCos[1], cent); + add_v3_v3(insetCos[2], cent); } -#endif //PROJ_DEBUG_NOSEAMBLEED +#endif //PROJ_DEBUG_NOSEAMBLEED static float len_squared_v2v2_alt(const float v1[2], const float v2_1, const float v2_2) { - float x, y; + float x, y; - x = v1[0] - v2_1; - y = v1[1] - v2_2; - return x * x + y * y; + x = v1[0] - v2_1; + y = v1[1] - v2_2; + return x * x + y * y; } /* note, use a squared value so we can use len_squared_v2v2 * be sure that you have done a bounds check first or this may fail */ /* only give bucket_bounds as an arg because we need it elsewhere */ -static bool project_bucket_isect_circle(const float cent[2], const float radius_squared, const rctf *bucket_bounds) +static bool project_bucket_isect_circle(const float cent[2], + const float radius_squared, + const rctf *bucket_bounds) { - /* Would normally to a simple intersection test, however we know the bounds of these 2 already intersect - * so we only need to test if the center is inside the vertical or horizontal bounds on either axis, - * this is even less work then an intersection test - */ + /* Would normally to a simple intersection test, however we know the bounds of these 2 already intersect + * so we only need to test if the center is inside the vertical or horizontal bounds on either axis, + * this is even less work then an intersection test + */ #if 0 - if (BLI_rctf_isect_pt_v(bucket_bounds, cent)) - return 1; + if (BLI_rctf_isect_pt_v(bucket_bounds, cent)) + return 1; #endif - if ((bucket_bounds->xmin <= cent[0] && bucket_bounds->xmax >= cent[0]) || - (bucket_bounds->ymin <= cent[1] && bucket_bounds->ymax >= cent[1])) - { - return 1; - } - - /* out of bounds left */ - if (cent[0] < bucket_bounds->xmin) { - /* lower left out of radius test */ - if (cent[1] < bucket_bounds->ymin) { - return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymin) < radius_squared) ? 1 : 0; - } - /* top left test */ - else if (cent[1] > bucket_bounds->ymax) { - return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymax) < radius_squared) ? 1 : 0; - } - } - else if (cent[0] > bucket_bounds->xmax) { - /* lower right out of radius test */ - if (cent[1] < bucket_bounds->ymin) { - return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymin) < radius_squared) ? 1 : 0; - } - /* top right test */ - else if (cent[1] > bucket_bounds->ymax) { - return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymax) < radius_squared) ? 1 : 0; - } - } - - return 0; + if ((bucket_bounds->xmin <= cent[0] && bucket_bounds->xmax >= cent[0]) || + (bucket_bounds->ymin <= cent[1] && bucket_bounds->ymax >= cent[1])) { + return 1; + } + + /* out of bounds left */ + if (cent[0] < bucket_bounds->xmin) { + /* lower left out of radius test */ + if (cent[1] < bucket_bounds->ymin) { + return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymin) < + radius_squared) ? + 1 : + 0; + } + /* top left test */ + else if (cent[1] > bucket_bounds->ymax) { + return (len_squared_v2v2_alt(cent, bucket_bounds->xmin, bucket_bounds->ymax) < + radius_squared) ? + 1 : + 0; + } + } + else if (cent[0] > bucket_bounds->xmax) { + /* lower right out of radius test */ + if (cent[1] < bucket_bounds->ymin) { + return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymin) < + radius_squared) ? + 1 : + 0; + } + /* top right test */ + else if (cent[1] > bucket_bounds->ymax) { + return (len_squared_v2v2_alt(cent, bucket_bounds->xmax, bucket_bounds->ymax) < + radius_squared) ? + 1 : + 0; + } + } + + return 0; } - - /* Note for rect_to_uvspace_ortho() and rect_to_uvspace_persp() * in ortho view this function gives good results when bucket_bounds are outside the triangle * however in some cases, perspective view will mess up with faces that have minimal screenspace area @@ -2234,70 +2273,75 @@ static bool project_bucket_isect_circle(const float cent[2], const float radius_ * funcs that only account for points inside the triangle. * however switching back to this for ortho is always an option */ -static void rect_to_uvspace_ortho( - const rctf *bucket_bounds, - const float *v1coSS, const float *v2coSS, const float *v3coSS, - const float *uv1co, const float *uv2co, const float *uv3co, - float bucket_bounds_uv[4][2], - const int flip) +static void rect_to_uvspace_ortho(const rctf *bucket_bounds, + const float *v1coSS, + const float *v2coSS, + const float *v3coSS, + const float *uv1co, + const float *uv2co, + const float *uv3co, + float bucket_bounds_uv[4][2], + const int flip) { - float uv[2]; - float w[3]; - - /* get the UV space bounding box */ - uv[0] = bucket_bounds->xmax; - uv[1] = bucket_bounds->ymin; - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmax; // set above - uv[1] = bucket_bounds->ymax; - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); - - uv[0] = bucket_bounds->xmin; - //uv[1] = bucket_bounds->ymax; // set above - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmin; // set above - uv[1] = bucket_bounds->ymin; - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); + float uv[2]; + float w[3]; + + /* get the UV space bounding box */ + uv[0] = bucket_bounds->xmax; + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmax; // set above + uv[1] = bucket_bounds->ymax; + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); + + uv[0] = bucket_bounds->xmin; + //uv[1] = bucket_bounds->ymax; // set above + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmin; // set above + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); } /* same as above but use barycentric_weights_v2_persp */ -static void rect_to_uvspace_persp( - const rctf *bucket_bounds, - const float *v1coSS, const float *v2coSS, const float *v3coSS, - const float *uv1co, const float *uv2co, const float *uv3co, - float bucket_bounds_uv[4][2], - const int flip - ) +static void rect_to_uvspace_persp(const rctf *bucket_bounds, + const float *v1coSS, + const float *v2coSS, + const float *v3coSS, + const float *uv1co, + const float *uv2co, + const float *uv3co, + float bucket_bounds_uv[4][2], + const int flip) { - float uv[2]; - float w[3]; - - /* get the UV space bounding box */ - uv[0] = bucket_bounds->xmax; - uv[1] = bucket_bounds->ymin; - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmax; // set above - uv[1] = bucket_bounds->ymax; - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); - - uv[0] = bucket_bounds->xmin; - //uv[1] = bucket_bounds->ymax; // set above - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); - - //uv[0] = bucket_bounds->xmin; // set above - uv[1] = bucket_bounds->ymin; - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); - interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); + float uv[2]; + float w[3]; + + /* get the UV space bounding box */ + uv[0] = bucket_bounds->xmax; + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 3 : 0], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmax; // set above + uv[1] = bucket_bounds->ymax; + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 2 : 1], uv1co, uv2co, uv3co, w); + + uv[0] = bucket_bounds->xmin; + //uv[1] = bucket_bounds->ymax; // set above + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 1 : 2], uv1co, uv2co, uv3co, w); + + //uv[0] = bucket_bounds->xmin; // set above + uv[1] = bucket_bounds->ymin; + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, uv, w); + interp_v2_v2v2v2(bucket_bounds_uv[flip ? 0 : 3], uv1co, uv2co, uv3co, w); } /* This works as we need it to but we can save a few steps and not use it */ @@ -2305,12 +2349,12 @@ static void rect_to_uvspace_persp( #if 0 static float angle_2d_clockwise(const float p1[2], const float p2[2], const float p3[2]) { - float v1[2], v2[2]; + float v1[2], v2[2]; - v1[0] = p1[0] - p2[0]; v1[1] = p1[1] - p2[1]; - v2[0] = p3[0] - p2[0]; v2[1] = p3[1] - p2[1]; + v1[0] = p1[0] - p2[0]; v1[1] = p1[1] - p2[1]; + v2[0] = p3[0] - p2[0]; v2[1] = p3[1] - p2[1]; - return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); + return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); } #endif @@ -2323,13 +2367,10 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa /* limit must be a fraction over 1.0f */ static bool IsectPT2Df_limit( - const float pt[2], - const float v1[2], const float v2[2], const float v3[2], - const float limit) + const float pt[2], const float v1[2], const float v2[2], const float v3[2], const float limit) { - return ((area_tri_v2(pt, v1, v2) + - area_tri_v2(pt, v2, v3) + - area_tri_v2(pt, v3, v1)) / (area_tri_v2(v1, v2, v3))) < limit; + return ((area_tri_v2(pt, v1, v2) + area_tri_v2(pt, v2, v3) + area_tri_v2(pt, v3, v1)) / + (area_tri_v2(v1, v2, v3))) < limit; } /* Clip the face by a bucket and set the uv-space bucket_bounds_uv @@ -2337,360 +2378,445 @@ static bool IsectPT2Df_limit( * */ static int float_z_sort_flip(const void *p1, const void *p2) { - return (((float *)p1)[2] < ((float *)p2)[2] ? 1 : -1); + return (((float *)p1)[2] < ((float *)p2)[2] ? 1 : -1); } static int float_z_sort(const void *p1, const void *p2) { - return (((float *)p1)[2] < ((float *)p2)[2] ? -1 : 1); + return (((float *)p1)[2] < ((float *)p2)[2] ? -1 : 1); } /* assumes one point is within the rectangle */ -static bool line_rect_clip( - const rctf *rect, - const float l1[4], const float l2[4], - const float uv1[2], const float uv2[2], - float uv[2], bool is_ortho) +static bool line_rect_clip(const rctf *rect, + const float l1[4], + const float l2[4], + const float uv1[2], + const float uv2[2], + float uv[2], + bool is_ortho) { - float min = FLT_MAX, tmp; - float xlen = l2[0] - l1[0]; - float ylen = l2[1] - l1[1]; - - /* 0.1 might seem too much, but remember, this is pixels! */ - if (xlen > 0.1f) { - if ((l1[0] - rect->xmin) * (l2[0] - rect->xmin) <= 0) { - tmp = rect->xmin; - min = min_ff((tmp - l1[0]) / xlen, min); - } - else if ((l1[0] - rect->xmax) * (l2[0] - rect->xmax) < 0) { - tmp = rect->xmax; - min = min_ff((tmp - l1[0]) / xlen, min); - } - } - - if (ylen > 0.1f) { - if ((l1[1] - rect->ymin) * (l2[1] - rect->ymin) <= 0) { - tmp = rect->ymin; - min = min_ff((tmp - l1[1]) / ylen, min); - } - else if ((l1[1] - rect->ymax) * (l2[1] - rect->ymax) < 0) { - tmp = rect->ymax; - min = min_ff((tmp - l1[1]) / ylen, min); - } - } - - if (min == FLT_MAX) - return false; - - tmp = (is_ortho) ? 1.0f : (l1[3] + min * (l2[3] - l1[3])); - - uv[0] = (uv1[0] + min / tmp * (uv2[0] - uv1[0])); - uv[1] = (uv1[1] + min / tmp * (uv2[1] - uv1[1])); - - return true; + float min = FLT_MAX, tmp; + float xlen = l2[0] - l1[0]; + float ylen = l2[1] - l1[1]; + + /* 0.1 might seem too much, but remember, this is pixels! */ + if (xlen > 0.1f) { + if ((l1[0] - rect->xmin) * (l2[0] - rect->xmin) <= 0) { + tmp = rect->xmin; + min = min_ff((tmp - l1[0]) / xlen, min); + } + else if ((l1[0] - rect->xmax) * (l2[0] - rect->xmax) < 0) { + tmp = rect->xmax; + min = min_ff((tmp - l1[0]) / xlen, min); + } + } + + if (ylen > 0.1f) { + if ((l1[1] - rect->ymin) * (l2[1] - rect->ymin) <= 0) { + tmp = rect->ymin; + min = min_ff((tmp - l1[1]) / ylen, min); + } + else if ((l1[1] - rect->ymax) * (l2[1] - rect->ymax) < 0) { + tmp = rect->ymax; + min = min_ff((tmp - l1[1]) / ylen, min); + } + } + + if (min == FLT_MAX) + return false; + + tmp = (is_ortho) ? 1.0f : (l1[3] + min * (l2[3] - l1[3])); + + uv[0] = (uv1[0] + min / tmp * (uv2[0] - uv1[0])); + uv[1] = (uv1[1] + min / tmp * (uv2[1] - uv1[1])); + + return true; } - -static void project_bucket_clip_face( - const bool is_ortho, const bool is_flip_object, - const rctf *cliprect, - const rctf *bucket_bounds, - const float *v1coSS, const float *v2coSS, const float *v3coSS, - const float *uv1co, const float *uv2co, const float *uv3co, - float bucket_bounds_uv[8][2], - int *tot, bool cull) +static void project_bucket_clip_face(const bool is_ortho, + const bool is_flip_object, + const rctf *cliprect, + const rctf *bucket_bounds, + const float *v1coSS, + const float *v2coSS, + const float *v3coSS, + const float *uv1co, + const float *uv2co, + const float *uv3co, + float bucket_bounds_uv[8][2], + int *tot, + bool cull) { - int inside_bucket_flag = 0; - int inside_face_flag = 0; - int flip; - bool collinear = false; - - float bucket_bounds_ss[4][2]; - - /* detect pathological case where face the three vertices are almost collinear in screen space. - * mostly those will be culled but when flood filling or with - * smooth shading it's a possibility */ - if (min_fff(dist_squared_to_line_v2(v1coSS, v2coSS, v3coSS), - dist_squared_to_line_v2(v2coSS, v3coSS, v1coSS), - dist_squared_to_line_v2(v3coSS, v1coSS, v2coSS)) < PROJ_PIXEL_TOLERANCE) - { - collinear = true; - } - - /* get the UV space bounding box */ - inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v1coSS); - inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v2coSS) << 1; - inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; - - if (inside_bucket_flag == ISECT_ALL3) { - /* is_flip_object is used here because we use the face winding */ - flip = (((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != is_flip_object) != - (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); - - /* all screenspace points are inside the bucket bounding box, - * this means we don't need to clip and can simply return the UVs */ - if (flip) { /* facing the back? */ - copy_v2_v2(bucket_bounds_uv[0], uv3co); - copy_v2_v2(bucket_bounds_uv[1], uv2co); - copy_v2_v2(bucket_bounds_uv[2], uv1co); - } - else { - copy_v2_v2(bucket_bounds_uv[0], uv1co); - copy_v2_v2(bucket_bounds_uv[1], uv2co); - copy_v2_v2(bucket_bounds_uv[2], uv3co); - } - - *tot = 3; - return; - } - /* Handle pathological case here, - * no need for further intersections below since triangle area is almost zero. */ - if (collinear) { - int flag; - - (*tot) = 0; - - if (cull) - return; - - if (inside_bucket_flag & ISECT_1) { copy_v2_v2(bucket_bounds_uv[*tot], uv1co); (*tot)++; } - - flag = inside_bucket_flag & (ISECT_1 | ISECT_2); - if (flag && flag != (ISECT_1 | ISECT_2)) { - if (line_rect_clip(bucket_bounds, v1coSS, v2coSS, uv1co, uv2co, bucket_bounds_uv[*tot], is_ortho)) - (*tot)++; - } - - if (inside_bucket_flag & ISECT_2) { copy_v2_v2(bucket_bounds_uv[*tot], uv2co); (*tot)++; } - - flag = inside_bucket_flag & (ISECT_2 | ISECT_3); - if (flag && flag != (ISECT_2 | ISECT_3)) { - if (line_rect_clip(bucket_bounds, v2coSS, v3coSS, uv2co, uv3co, bucket_bounds_uv[*tot], is_ortho)) - (*tot)++; - } - - if (inside_bucket_flag & ISECT_3) { copy_v2_v2(bucket_bounds_uv[*tot], uv3co); (*tot)++; } - - flag = inside_bucket_flag & (ISECT_3 | ISECT_1); - if (flag && flag != (ISECT_3 | ISECT_1)) { - if (line_rect_clip(bucket_bounds, v3coSS, v1coSS, uv3co, uv1co, bucket_bounds_uv[*tot], is_ortho)) - (*tot)++; - } - - if ((*tot) < 3) { - /* no intersections to speak of, but more probable is that all face is just outside the - * rectangle and culled due to float precision issues. Since above tests have failed, - * just dump triangle as is for painting */ - *tot = 0; - copy_v2_v2(bucket_bounds_uv[*tot], uv1co); (*tot)++; - copy_v2_v2(bucket_bounds_uv[*tot], uv2co); (*tot)++; - copy_v2_v2(bucket_bounds_uv[*tot], uv3co); (*tot)++; - return; - } - - return; - } - - /* get the UV space bounding box */ - /* use IsectPT2Df_limit here so we catch points are are touching the tri edge - * (or a small fraction over) */ - bucket_bounds_ss[0][0] = bucket_bounds->xmax; - bucket_bounds_ss[0][1] = bucket_bounds->ymin; - inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[0], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? ISECT_1 : 0); - - bucket_bounds_ss[1][0] = bucket_bounds->xmax; - bucket_bounds_ss[1][1] = bucket_bounds->ymax; - inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[1], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? ISECT_2 : 0); - - bucket_bounds_ss[2][0] = bucket_bounds->xmin; - bucket_bounds_ss[2][1] = bucket_bounds->ymax; - inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[2], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? ISECT_3 : 0); - - bucket_bounds_ss[3][0] = bucket_bounds->xmin; - bucket_bounds_ss[3][1] = bucket_bounds->ymin; - inside_face_flag |= (IsectPT2Df_limit(bucket_bounds_ss[3], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? ISECT_4 : 0); - - flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != - (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); - - if (inside_face_flag == ISECT_ALL4) { - /* bucket is totally inside the screenspace face, we can safely use weights */ - - if (is_ortho) { - rect_to_uvspace_ortho(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip); - } - else { - rect_to_uvspace_persp(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip); - } - - *tot = 4; - return; - } - else { - /* The Complicated Case! - * - * The 2 cases above are where the face is inside the bucket - * or the bucket is inside the face. - * - * we need to make a convex polyline from the intersection between the screenspace face - * and the bucket bounds. - * - * There are a number of ways this could be done, currently it just collects all - * intersecting verts, and line intersections, then sorts them clockwise, this is - * a lot easier then evaluating the geometry to do a correct clipping on both shapes. - */ - - - /* Add a bunch of points, we know must make up the convex hull - * which is the clipped rect and triangle */ - - /* Maximum possible 6 intersections when using a rectangle and triangle */ - - /* The 3rd float is used to store angle for qsort(), NOT as a Z location */ - float isectVCosSS[8][3]; - float v1_clipSS[2], v2_clipSS[2]; - float w[3]; - - /* calc center */ - float cent[2] = {0.0f, 0.0f}; - /*float up[2] = {0.0f, 1.0f};*/ - int i; - bool doubles; - - (*tot) = 0; - - if (inside_face_flag & ISECT_1) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[0]); (*tot)++; } - if (inside_face_flag & ISECT_2) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[1]); (*tot)++; } - if (inside_face_flag & ISECT_3) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[2]); (*tot)++; } - if (inside_face_flag & ISECT_4) { copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[3]); (*tot)++; } - - if (inside_bucket_flag & ISECT_1) { copy_v2_v2(isectVCosSS[*tot], v1coSS); (*tot)++; } - if (inside_bucket_flag & ISECT_2) { copy_v2_v2(isectVCosSS[*tot], v2coSS); (*tot)++; } - if (inside_bucket_flag & ISECT_3) { copy_v2_v2(isectVCosSS[*tot], v3coSS); (*tot)++; } - - if ((inside_bucket_flag & (ISECT_1 | ISECT_2)) != (ISECT_1 | ISECT_2)) { - if (line_clip_rect2f(cliprect, bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { - if ((inside_bucket_flag & ISECT_1) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } - if ((inside_bucket_flag & ISECT_2) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } - } - } - - if ((inside_bucket_flag & (ISECT_2 | ISECT_3)) != (ISECT_2 | ISECT_3)) { - if (line_clip_rect2f(cliprect, bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { - if ((inside_bucket_flag & ISECT_2) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } - if ((inside_bucket_flag & ISECT_3) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } - } - } - - if ((inside_bucket_flag & (ISECT_3 | ISECT_1)) != (ISECT_3 | ISECT_1)) { - if (line_clip_rect2f(cliprect, bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { - if ((inside_bucket_flag & ISECT_3) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } - if ((inside_bucket_flag & ISECT_1) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } - } - } - - - if ((*tot) < 3) { /* no intersections to speak of */ - *tot = 0; - return; - } - - /* now we have all points we need, collect their angles and sort them clockwise */ - - for (i = 0; i < (*tot); i++) { - cent[0] += isectVCosSS[i][0]; - cent[1] += isectVCosSS[i][1]; - } - cent[0] = cent[0] / (float)(*tot); - cent[1] = cent[1] / (float)(*tot); - - - - /* Collect angles for every point around the center point */ - - -#if 0 /* uses a few more cycles then the above loop */ - for (i = 0; i < (*tot); i++) { - isectVCosSS[i][2] = angle_2d_clockwise(up, cent, isectVCosSS[i]); - } + int inside_bucket_flag = 0; + int inside_face_flag = 0; + int flip; + bool collinear = false; + + float bucket_bounds_ss[4][2]; + + /* detect pathological case where face the three vertices are almost collinear in screen space. + * mostly those will be culled but when flood filling or with + * smooth shading it's a possibility */ + if (min_fff(dist_squared_to_line_v2(v1coSS, v2coSS, v3coSS), + dist_squared_to_line_v2(v2coSS, v3coSS, v1coSS), + dist_squared_to_line_v2(v3coSS, v1coSS, v2coSS)) < PROJ_PIXEL_TOLERANCE) { + collinear = true; + } + + /* get the UV space bounding box */ + inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v1coSS); + inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v2coSS) << 1; + inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; + + if (inside_bucket_flag == ISECT_ALL3) { + /* is_flip_object is used here because we use the face winding */ + flip = (((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != is_flip_object) != + (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); + + /* all screenspace points are inside the bucket bounding box, + * this means we don't need to clip and can simply return the UVs */ + if (flip) { /* facing the back? */ + copy_v2_v2(bucket_bounds_uv[0], uv3co); + copy_v2_v2(bucket_bounds_uv[1], uv2co); + copy_v2_v2(bucket_bounds_uv[2], uv1co); + } + else { + copy_v2_v2(bucket_bounds_uv[0], uv1co); + copy_v2_v2(bucket_bounds_uv[1], uv2co); + copy_v2_v2(bucket_bounds_uv[2], uv3co); + } + + *tot = 3; + return; + } + /* Handle pathological case here, + * no need for further intersections below since triangle area is almost zero. */ + if (collinear) { + int flag; + + (*tot) = 0; + + if (cull) + return; + + if (inside_bucket_flag & ISECT_1) { + copy_v2_v2(bucket_bounds_uv[*tot], uv1co); + (*tot)++; + } + + flag = inside_bucket_flag & (ISECT_1 | ISECT_2); + if (flag && flag != (ISECT_1 | ISECT_2)) { + if (line_rect_clip( + bucket_bounds, v1coSS, v2coSS, uv1co, uv2co, bucket_bounds_uv[*tot], is_ortho)) + (*tot)++; + } + + if (inside_bucket_flag & ISECT_2) { + copy_v2_v2(bucket_bounds_uv[*tot], uv2co); + (*tot)++; + } + + flag = inside_bucket_flag & (ISECT_2 | ISECT_3); + if (flag && flag != (ISECT_2 | ISECT_3)) { + if (line_rect_clip( + bucket_bounds, v2coSS, v3coSS, uv2co, uv3co, bucket_bounds_uv[*tot], is_ortho)) + (*tot)++; + } + + if (inside_bucket_flag & ISECT_3) { + copy_v2_v2(bucket_bounds_uv[*tot], uv3co); + (*tot)++; + } + + flag = inside_bucket_flag & (ISECT_3 | ISECT_1); + if (flag && flag != (ISECT_3 | ISECT_1)) { + if (line_rect_clip( + bucket_bounds, v3coSS, v1coSS, uv3co, uv1co, bucket_bounds_uv[*tot], is_ortho)) + (*tot)++; + } + + if ((*tot) < 3) { + /* no intersections to speak of, but more probable is that all face is just outside the + * rectangle and culled due to float precision issues. Since above tests have failed, + * just dump triangle as is for painting */ + *tot = 0; + copy_v2_v2(bucket_bounds_uv[*tot], uv1co); + (*tot)++; + copy_v2_v2(bucket_bounds_uv[*tot], uv2co); + (*tot)++; + copy_v2_v2(bucket_bounds_uv[*tot], uv3co); + (*tot)++; + return; + } + + return; + } + + /* get the UV space bounding box */ + /* use IsectPT2Df_limit here so we catch points are are touching the tri edge + * (or a small fraction over) */ + bucket_bounds_ss[0][0] = bucket_bounds->xmax; + bucket_bounds_ss[0][1] = bucket_bounds->ymin; + inside_face_flag |= (IsectPT2Df_limit( + bucket_bounds_ss[0], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? + ISECT_1 : + 0); + + bucket_bounds_ss[1][0] = bucket_bounds->xmax; + bucket_bounds_ss[1][1] = bucket_bounds->ymax; + inside_face_flag |= (IsectPT2Df_limit( + bucket_bounds_ss[1], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? + ISECT_2 : + 0); + + bucket_bounds_ss[2][0] = bucket_bounds->xmin; + bucket_bounds_ss[2][1] = bucket_bounds->ymax; + inside_face_flag |= (IsectPT2Df_limit( + bucket_bounds_ss[2], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? + ISECT_3 : + 0); + + bucket_bounds_ss[3][0] = bucket_bounds->xmin; + bucket_bounds_ss[3][1] = bucket_bounds->ymin; + inside_face_flag |= (IsectPT2Df_limit( + bucket_bounds_ss[3], v1coSS, v2coSS, v3coSS, 1 + PROJ_GEOM_TOLERANCE) ? + ISECT_4 : + 0); + + flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != + (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); + + if (inside_face_flag == ISECT_ALL4) { + /* bucket is totally inside the screenspace face, we can safely use weights */ + + if (is_ortho) { + rect_to_uvspace_ortho( + bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip); + } + else { + rect_to_uvspace_persp( + bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, bucket_bounds_uv, flip); + } + + *tot = 4; + return; + } + else { + /* The Complicated Case! + * + * The 2 cases above are where the face is inside the bucket + * or the bucket is inside the face. + * + * we need to make a convex polyline from the intersection between the screenspace face + * and the bucket bounds. + * + * There are a number of ways this could be done, currently it just collects all + * intersecting verts, and line intersections, then sorts them clockwise, this is + * a lot easier then evaluating the geometry to do a correct clipping on both shapes. + */ + + /* Add a bunch of points, we know must make up the convex hull + * which is the clipped rect and triangle */ + + /* Maximum possible 6 intersections when using a rectangle and triangle */ + + /* The 3rd float is used to store angle for qsort(), NOT as a Z location */ + float isectVCosSS[8][3]; + float v1_clipSS[2], v2_clipSS[2]; + float w[3]; + + /* calc center */ + float cent[2] = {0.0f, 0.0f}; + /*float up[2] = {0.0f, 1.0f};*/ + int i; + bool doubles; + + (*tot) = 0; + + if (inside_face_flag & ISECT_1) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[0]); + (*tot)++; + } + if (inside_face_flag & ISECT_2) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[1]); + (*tot)++; + } + if (inside_face_flag & ISECT_3) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[2]); + (*tot)++; + } + if (inside_face_flag & ISECT_4) { + copy_v2_v2(isectVCosSS[*tot], bucket_bounds_ss[3]); + (*tot)++; + } + + if (inside_bucket_flag & ISECT_1) { + copy_v2_v2(isectVCosSS[*tot], v1coSS); + (*tot)++; + } + if (inside_bucket_flag & ISECT_2) { + copy_v2_v2(isectVCosSS[*tot], v2coSS); + (*tot)++; + } + if (inside_bucket_flag & ISECT_3) { + copy_v2_v2(isectVCosSS[*tot], v3coSS); + (*tot)++; + } + + if ((inside_bucket_flag & (ISECT_1 | ISECT_2)) != (ISECT_1 | ISECT_2)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { + if ((inside_bucket_flag & ISECT_1) == 0) { + copy_v2_v2(isectVCosSS[*tot], v1_clipSS); + (*tot)++; + } + if ((inside_bucket_flag & ISECT_2) == 0) { + copy_v2_v2(isectVCosSS[*tot], v2_clipSS); + (*tot)++; + } + } + } + + if ((inside_bucket_flag & (ISECT_2 | ISECT_3)) != (ISECT_2 | ISECT_3)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { + if ((inside_bucket_flag & ISECT_2) == 0) { + copy_v2_v2(isectVCosSS[*tot], v1_clipSS); + (*tot)++; + } + if ((inside_bucket_flag & ISECT_3) == 0) { + copy_v2_v2(isectVCosSS[*tot], v2_clipSS); + (*tot)++; + } + } + } + + if ((inside_bucket_flag & (ISECT_3 | ISECT_1)) != (ISECT_3 | ISECT_1)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { + if ((inside_bucket_flag & ISECT_3) == 0) { + copy_v2_v2(isectVCosSS[*tot], v1_clipSS); + (*tot)++; + } + if ((inside_bucket_flag & ISECT_1) == 0) { + copy_v2_v2(isectVCosSS[*tot], v2_clipSS); + (*tot)++; + } + } + } + + if ((*tot) < 3) { /* no intersections to speak of */ + *tot = 0; + return; + } + + /* now we have all points we need, collect their angles and sort them clockwise */ + + for (i = 0; i < (*tot); i++) { + cent[0] += isectVCosSS[i][0]; + cent[1] += isectVCosSS[i][1]; + } + cent[0] = cent[0] / (float)(*tot); + cent[1] = cent[1] / (float)(*tot); + + /* Collect angles for every point around the center point */ + +#if 0 /* uses a few more cycles then the above loop */ + for (i = 0; i < (*tot); i++) { + isectVCosSS[i][2] = angle_2d_clockwise(up, cent, isectVCosSS[i]); + } #endif - /* Abuse this var for the loop below */ - v1_clipSS[0] = cent[0]; - v1_clipSS[1] = cent[1] + 1.0f; - - for (i = 0; i < (*tot); i++) { - v2_clipSS[0] = isectVCosSS[i][0] - cent[0]; - v2_clipSS[1] = isectVCosSS[i][1] - cent[1]; - isectVCosSS[i][2] = atan2f(v1_clipSS[0] * v2_clipSS[1] - v1_clipSS[1] * v2_clipSS[0], - v1_clipSS[0] * v2_clipSS[0] + v1_clipSS[1] * v2_clipSS[1]); - } - - if (flip) qsort(isectVCosSS, *tot, sizeof(float) * 3, float_z_sort_flip); - else qsort(isectVCosSS, *tot, sizeof(float) * 3, float_z_sort); - - doubles = true; - while (doubles == true) { - doubles = false; - - for (i = 0; i < (*tot); i++) { - if (fabsf(isectVCosSS[(i + 1) % *tot][0] - isectVCosSS[i][0]) < PROJ_PIXEL_TOLERANCE && - fabsf(isectVCosSS[(i + 1) % *tot][1] - isectVCosSS[i][1]) < PROJ_PIXEL_TOLERANCE) - { - int j; - for (j = i; j < (*tot) - 1; j++) { - isectVCosSS[j][0] = isectVCosSS[j + 1][0]; - isectVCosSS[j][1] = isectVCosSS[j + 1][1]; - } - /* keep looking for more doubles */ - doubles = true; - (*tot)--; - } - } - - /* its possible there is only a few left after remove doubles */ - if ((*tot) < 3) { - // printf("removed too many doubles B\n"); - *tot = 0; - return; - } - } - - if (is_ortho) { - for (i = 0; i < (*tot); i++) { - barycentric_weights_v2(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); - interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); - } - } - else { - for (i = 0; i < (*tot); i++) { - barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); - interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); - } - } - } + /* Abuse this var for the loop below */ + v1_clipSS[0] = cent[0]; + v1_clipSS[1] = cent[1] + 1.0f; + + for (i = 0; i < (*tot); i++) { + v2_clipSS[0] = isectVCosSS[i][0] - cent[0]; + v2_clipSS[1] = isectVCosSS[i][1] - cent[1]; + isectVCosSS[i][2] = atan2f(v1_clipSS[0] * v2_clipSS[1] - v1_clipSS[1] * v2_clipSS[0], + v1_clipSS[0] * v2_clipSS[0] + v1_clipSS[1] * v2_clipSS[1]); + } + + if (flip) + qsort(isectVCosSS, *tot, sizeof(float) * 3, float_z_sort_flip); + else + qsort(isectVCosSS, *tot, sizeof(float) * 3, float_z_sort); + + doubles = true; + while (doubles == true) { + doubles = false; + + for (i = 0; i < (*tot); i++) { + if (fabsf(isectVCosSS[(i + 1) % *tot][0] - isectVCosSS[i][0]) < PROJ_PIXEL_TOLERANCE && + fabsf(isectVCosSS[(i + 1) % *tot][1] - isectVCosSS[i][1]) < PROJ_PIXEL_TOLERANCE) { + int j; + for (j = i; j < (*tot) - 1; j++) { + isectVCosSS[j][0] = isectVCosSS[j + 1][0]; + isectVCosSS[j][1] = isectVCosSS[j + 1][1]; + } + /* keep looking for more doubles */ + doubles = true; + (*tot)--; + } + } + + /* its possible there is only a few left after remove doubles */ + if ((*tot) < 3) { + // printf("removed too many doubles B\n"); + *tot = 0; + return; + } + } + + if (is_ortho) { + for (i = 0; i < (*tot); i++) { + barycentric_weights_v2(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); + interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); + } + } + else { + for (i = 0; i < (*tot); i++) { + barycentric_weights_v2_persp(v1coSS, v2coSS, v3coSS, isectVCosSS[i], w); + interp_v2_v2v2v2(bucket_bounds_uv[i], uv1co, uv2co, uv3co, w); + } + } + } #ifdef PROJ_DEBUG_PRINT_CLIP - /* include this at the bottom of the above function to debug the output */ - - { - /* If there are ever any problems, */ - float test_uv[4][2]; - int i; - if (is_ortho) rect_to_uvspace_ortho(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip); - else rect_to_uvspace_persp(bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip); - printf("( [(%f,%f), (%f,%f), (%f,%f), (%f,%f)], ", - test_uv[0][0], test_uv[0][1], test_uv[1][0], test_uv[1][1], - test_uv[2][0], test_uv[2][1], test_uv[3][0], test_uv[3][1]); - - printf(" [(%f,%f), (%f,%f), (%f,%f)], ", uv1co[0], uv1co[1], uv2co[0], uv2co[1], uv3co[0], uv3co[1]); - - printf("["); - for (i = 0; i < (*tot); i++) { - printf("(%f, %f),", bucket_bounds_uv[i][0], bucket_bounds_uv[i][1]); - } - printf("]),\\\n"); - } + /* include this at the bottom of the above function to debug the output */ + + { + /* If there are ever any problems, */ + float test_uv[4][2]; + int i; + if (is_ortho) + rect_to_uvspace_ortho( + bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip); + else + rect_to_uvspace_persp( + bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, test_uv, flip); + printf("( [(%f,%f), (%f,%f), (%f,%f), (%f,%f)], ", + test_uv[0][0], + test_uv[0][1], + test_uv[1][0], + test_uv[1][1], + test_uv[2][0], + test_uv[2][1], + test_uv[3][0], + test_uv[3][1]); + + printf(" [(%f,%f), (%f,%f), (%f,%f)], ", + uv1co[0], + uv1co[1], + uv2co[0], + uv2co[1], + uv3co[0], + uv3co[1]); + + printf("["); + for (i = 0; i < (*tot); i++) { + printf("(%f, %f),", bucket_bounds_uv[i][0], bucket_bounds_uv[i][1]); + } + printf("]),\\\n"); + } #endif } @@ -2740,7 +2866,6 @@ static void project_bucket_clip_face( * main() */ - #undef ISECT_1 #undef ISECT_2 #undef ISECT_3 @@ -2748,35 +2873,32 @@ static void project_bucket_clip_face( #undef ISECT_ALL3 #undef ISECT_ALL4 - /* checks if pt is inside a convex 2D polyline, the polyline must be ordered rotating clockwise * otherwise it would have to test for mixed (line_point_side_v2 > 0.0f) cases */ static bool IsectPoly2Df(const float pt[2], float uv[][2], const int tot) { - int i; - if (line_point_side_v2(uv[tot - 1], uv[0], pt) < 0.0f) - return 0; + int i; + if (line_point_side_v2(uv[tot - 1], uv[0], pt) < 0.0f) + return 0; - for (i = 1; i < tot; i++) { - if (line_point_side_v2(uv[i - 1], uv[i], pt) < 0.0f) - return 0; + for (i = 1; i < tot; i++) { + if (line_point_side_v2(uv[i - 1], uv[i], pt) < 0.0f) + return 0; + } - } - - return 1; + return 1; } static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot) { - int i; - bool side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); - - for (i = 1; i < tot; i++) { - if ((line_point_side_v2(uv[i - 1], uv[i], pt) > 0.0f) != side) - return 0; + int i; + bool side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); - } + for (i = 1; i < tot; i++) { + if ((line_point_side_v2(uv[i - 1], uv[i], pt) > 0.0f) != side) + return 0; + } - return 1; + return 1; } /* One of the most important function for projection painting, @@ -2784,1824 +2906,1853 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot * * initialize pixels from this face where it intersects with the bucket_index, * optionally initialize pixels for removing seams */ -static void project_paint_face_init( - const ProjPaintState *ps, - const int thread_index, const int bucket_index, const int tri_index, const int image_index, - const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf) +static void project_paint_face_init(const ProjPaintState *ps, + const int thread_index, + const int bucket_index, + const int tri_index, + const int image_index, + const rctf *clip_rect, + const rctf *bucket_bounds, + ImBuf *ibuf, + ImBuf **tmpibuf) { - /* Projection vars, to get the 3D locations into screen space */ - MemArena *arena = ps->arena_mt[thread_index]; - LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index; - LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index]; - bool threaded = (ps->thread_tot > 1); - - TileInfo tinf = { - ps->tile_lock, - ps->do_masking, - IMAPAINT_TILE_NUMBER(ibuf->x), - tmpibuf, - ps->projImages + image_index, - }; - - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - - /* UV/pixel seeking data */ - /* Image X/Y-Pixel */ - int x, y; - float mask; - /* Image floating point UV - same as x, y but from 0.0-1.0 */ - float uv[2]; - - /* vert co screen-space, these will be assigned to lt_vtri[0-2] */ - const float *v1coSS, *v2coSS, *v3coSS; - - /* vertex screenspace coords */ - const float *vCo[3]; - - float w[3], wco[3]; - - /* for convenience only, these will be assigned to lt_tri_uv[0],1,2 or lt_tri_uv[0],2,3 */ - float *uv1co, *uv2co, *uv3co; - float pixelScreenCo[4]; - bool do_3d_mapping = ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D; - - /* ispace bounds */ - rcti bounds_px; - /* vars for getting uvspace bounds */ - - /* bucket bounds in UV space so we can init pixels only for this face, */ - float lt_uv_pxoffset[3][2]; - float xhalfpx, yhalfpx; - const float ibuf_xf = (float)ibuf->x, ibuf_yf = (float)ibuf->y; - - /* for early loop exit */ - int has_x_isect = 0, has_isect = 0; - - float uv_clip[8][2]; - int uv_clip_tot; - const bool is_ortho = ps->is_ortho; - const bool is_flip_object = ps->is_flip_object; - const bool do_backfacecull = ps->do_backfacecull; - const bool do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; - - vCo[0] = ps->mvert_eval[lt_vtri[0]].co; - vCo[1] = ps->mvert_eval[lt_vtri[1]].co; - vCo[2] = ps->mvert_eval[lt_vtri[2]].co; - - - /* Use lt_uv_pxoffset instead of lt_tri_uv so we can offset the UV half a pixel - * this is done so we can avoid offsetting all the pixels by 0.5 which causes - * problems when wrapping negative coords */ - xhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf; - yhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf; - - /* Note about (PROJ_GEOM_TOLERANCE/x) above... - * Needed to add this offset since UV coords are often quads aligned to pixels. - * In this case pixels can be exactly between 2 triangles causing nasty - * artifacts. - * - * This workaround can be removed and painting will still work on most cases - * but since the first thing most people try is painting onto a quad- better make it work. - */ - - lt_uv_pxoffset[0][0] = lt_tri_uv[0][0] - xhalfpx; - lt_uv_pxoffset[0][1] = lt_tri_uv[0][1] - yhalfpx; - - lt_uv_pxoffset[1][0] = lt_tri_uv[1][0] - xhalfpx; - lt_uv_pxoffset[1][1] = lt_tri_uv[1][1] - yhalfpx; - - lt_uv_pxoffset[2][0] = lt_tri_uv[2][0] - xhalfpx; - lt_uv_pxoffset[2][1] = lt_tri_uv[2][1] - yhalfpx; - - { - uv1co = lt_uv_pxoffset[0]; // was lt_tri_uv[i1]; - uv2co = lt_uv_pxoffset[1]; // was lt_tri_uv[i2]; - uv3co = lt_uv_pxoffset[2]; // was lt_tri_uv[i3]; - - v1coSS = ps->screenCoords[lt_vtri[0]]; - v2coSS = ps->screenCoords[lt_vtri[1]]; - v3coSS = ps->screenCoords[lt_vtri[2]]; - - /* This function gives is a concave polyline in UV space from the clipped tri*/ - project_bucket_clip_face( - is_ortho, is_flip_object, - clip_rect, bucket_bounds, - v1coSS, v2coSS, v3coSS, - uv1co, uv2co, uv3co, - uv_clip, &uv_clip_tot, - do_backfacecull || ps->do_occlude); - - /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */ + /* Projection vars, to get the 3D locations into screen space */ + MemArena *arena = ps->arena_mt[thread_index]; + LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index; + LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index]; + bool threaded = (ps->thread_tot > 1); + + TileInfo tinf = { + ps->tile_lock, + ps->do_masking, + IMAPAINT_TILE_NUMBER(ibuf->x), + tmpibuf, + ps->projImages + image_index, + }; + + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + + /* UV/pixel seeking data */ + /* Image X/Y-Pixel */ + int x, y; + float mask; + /* Image floating point UV - same as x, y but from 0.0-1.0 */ + float uv[2]; + + /* vert co screen-space, these will be assigned to lt_vtri[0-2] */ + const float *v1coSS, *v2coSS, *v3coSS; + + /* vertex screenspace coords */ + const float *vCo[3]; + + float w[3], wco[3]; + + /* for convenience only, these will be assigned to lt_tri_uv[0],1,2 or lt_tri_uv[0],2,3 */ + float *uv1co, *uv2co, *uv3co; + float pixelScreenCo[4]; + bool do_3d_mapping = ps->brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D; + + /* ispace bounds */ + rcti bounds_px; + /* vars for getting uvspace bounds */ + + /* bucket bounds in UV space so we can init pixels only for this face, */ + float lt_uv_pxoffset[3][2]; + float xhalfpx, yhalfpx; + const float ibuf_xf = (float)ibuf->x, ibuf_yf = (float)ibuf->y; + + /* for early loop exit */ + int has_x_isect = 0, has_isect = 0; + + float uv_clip[8][2]; + int uv_clip_tot; + const bool is_ortho = ps->is_ortho; + const bool is_flip_object = ps->is_flip_object; + const bool do_backfacecull = ps->do_backfacecull; + const bool do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; + + vCo[0] = ps->mvert_eval[lt_vtri[0]].co; + vCo[1] = ps->mvert_eval[lt_vtri[1]].co; + vCo[2] = ps->mvert_eval[lt_vtri[2]].co; + + /* Use lt_uv_pxoffset instead of lt_tri_uv so we can offset the UV half a pixel + * this is done so we can avoid offsetting all the pixels by 0.5 which causes + * problems when wrapping negative coords */ + xhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf; + yhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf; + + /* Note about (PROJ_GEOM_TOLERANCE/x) above... + * Needed to add this offset since UV coords are often quads aligned to pixels. + * In this case pixels can be exactly between 2 triangles causing nasty + * artifacts. + * + * This workaround can be removed and painting will still work on most cases + * but since the first thing most people try is painting onto a quad- better make it work. + */ + + lt_uv_pxoffset[0][0] = lt_tri_uv[0][0] - xhalfpx; + lt_uv_pxoffset[0][1] = lt_tri_uv[0][1] - yhalfpx; + + lt_uv_pxoffset[1][0] = lt_tri_uv[1][0] - xhalfpx; + lt_uv_pxoffset[1][1] = lt_tri_uv[1][1] - yhalfpx; + + lt_uv_pxoffset[2][0] = lt_tri_uv[2][0] - xhalfpx; + lt_uv_pxoffset[2][1] = lt_tri_uv[2][1] - yhalfpx; + + { + uv1co = lt_uv_pxoffset[0]; // was lt_tri_uv[i1]; + uv2co = lt_uv_pxoffset[1]; // was lt_tri_uv[i2]; + uv3co = lt_uv_pxoffset[2]; // was lt_tri_uv[i3]; + + v1coSS = ps->screenCoords[lt_vtri[0]]; + v2coSS = ps->screenCoords[lt_vtri[1]]; + v3coSS = ps->screenCoords[lt_vtri[2]]; + + /* This function gives is a concave polyline in UV space from the clipped tri*/ + project_bucket_clip_face(is_ortho, + is_flip_object, + clip_rect, + bucket_bounds, + v1coSS, + v2coSS, + v3coSS, + uv1co, + uv2co, + uv3co, + uv_clip, + &uv_clip_tot, + do_backfacecull || ps->do_occlude); + + /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */ #if 0 - if (uv_clip_tot > 6) { - printf("this should never happen! %d\n", uv_clip_tot); - } + if (uv_clip_tot > 6) { + printf("this should never happen! %d\n", uv_clip_tot); + } #endif - if (pixel_bounds_array(uv_clip, &bounds_px, ibuf->x, ibuf->y, uv_clip_tot)) { + if (pixel_bounds_array(uv_clip, &bounds_px, ibuf->x, ibuf->y, uv_clip_tot)) { #if 0 - project_paint_undo_tiles_init( - &bounds_px, ps->projImages + image_index, tmpibuf, - tile_width, threaded, ps->do_masking); + project_paint_undo_tiles_init( + &bounds_px, ps->projImages + image_index, tmpibuf, + tile_width, threaded, ps->do_masking); #endif - /* clip face and */ - - has_isect = 0; - for (y = bounds_px.ymin; y < bounds_px.ymax; y++) { - //uv[1] = (((float)y) + 0.5f) / (float)ibuf->y; - /* use pixel offset UV coords instead */ - uv[1] = (float)y / ibuf_yf; - - has_x_isect = 0; - for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { - //uv[0] = (((float)x) + 0.5f) / ibuf->x; - /* use pixel offset UV coords instead */ - uv[0] = (float)x / ibuf_xf; - - /* Note about IsectPoly2Df_twoside, checking the face or uv flipping doesn't work, - * could check the poly direction but better to do this */ - if ((do_backfacecull == true && IsectPoly2Df(uv, uv_clip, uv_clip_tot)) || - (do_backfacecull == false && IsectPoly2Df_twoside(uv, uv_clip, uv_clip_tot))) - { - - has_x_isect = has_isect = 1; - - if (is_ortho) screen_px_from_ortho(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); - else screen_px_from_persp(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); - - /* a pity we need to get the worldspace pixel location here */ - if (do_clip || do_3d_mapping) { - interp_v3_v3v3v3( - wco, - ps->mvert_eval[lt_vtri[0]].co, - ps->mvert_eval[lt_vtri[1]].co, - ps->mvert_eval[lt_vtri[2]].co, - w); - if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { - /* Watch out that no code below this needs to run */ - continue; - } - } - - /* Is this UV visible from the view? - raytrace */ - /* project_paint_PickFace is less complex, use for testing */ - //if (project_paint_PickFace(ps, pixelScreenCo, w, &side) == tri_index) { - if ((ps->do_occlude == false) || - !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixelScreenCo)) - { - mask = project_paint_uvpixel_mask(ps, tri_index, w); - - if (mask > 0.0f) { - BLI_linklist_prepend_arena( - bucketPixelNodes, - project_paint_uvpixel_init( - ps, arena, &tinf, x, y, mask, tri_index, - pixelScreenCo, wco, w), - arena); - } - } - - } -//#if 0 - else if (has_x_isect) { - /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ - break; - } -//#endif - } - - -#if 0 /* TODO - investigate why this dosnt work sometimes! it should! */ - /* no intersection for this entire row, - * after some intersection above means we can quit now */ - if (has_x_isect == 0 && has_isect) { - break; - } + /* clip face and */ + + has_isect = 0; + for (y = bounds_px.ymin; y < bounds_px.ymax; y++) { + //uv[1] = (((float)y) + 0.5f) / (float)ibuf->y; + /* use pixel offset UV coords instead */ + uv[1] = (float)y / ibuf_yf; + + has_x_isect = 0; + for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { + //uv[0] = (((float)x) + 0.5f) / ibuf->x; + /* use pixel offset UV coords instead */ + uv[0] = (float)x / ibuf_xf; + + /* Note about IsectPoly2Df_twoside, checking the face or uv flipping doesn't work, + * could check the poly direction but better to do this */ + if ((do_backfacecull == true && IsectPoly2Df(uv, uv_clip, uv_clip_tot)) || + (do_backfacecull == false && IsectPoly2Df_twoside(uv, uv_clip, uv_clip_tot))) { + + has_x_isect = has_isect = 1; + + if (is_ortho) + screen_px_from_ortho( + uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); + else + screen_px_from_persp( + uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); + + /* a pity we need to get the worldspace pixel location here */ + if (do_clip || do_3d_mapping) { + interp_v3_v3v3v3(wco, + ps->mvert_eval[lt_vtri[0]].co, + ps->mvert_eval[lt_vtri[1]].co, + ps->mvert_eval[lt_vtri[2]].co, + w); + if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { + /* Watch out that no code below this needs to run */ + continue; + } + } + + /* Is this UV visible from the view? - raytrace */ + /* project_paint_PickFace is less complex, use for testing */ + //if (project_paint_PickFace(ps, pixelScreenCo, w, &side) == tri_index) { + if ((ps->do_occlude == false) || + !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixelScreenCo)) { + mask = project_paint_uvpixel_mask(ps, tri_index, w); + + if (mask > 0.0f) { + BLI_linklist_prepend_arena( + bucketPixelNodes, + project_paint_uvpixel_init( + ps, arena, &tinf, x, y, mask, tri_index, pixelScreenCo, wco, w), + arena); + } + } + } + //#if 0 + else if (has_x_isect) { + /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + break; + } + //#endif + } + +#if 0 /* TODO - investigate why this dosnt work sometimes! it should! */ + /* no intersection for this entire row, + * after some intersection above means we can quit now */ + if (has_x_isect == 0 && has_isect) { + break; + } #endif - } - } - } - + } + } + } #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->seam_bleed_px > 0.0f && !(ps->faceSeamFlags[tri_index] & PROJ_FACE_DEGENERATE)) { - int face_seam_flag; - - if (threaded) { - /* Other threads could be modifying these vars. */ - BLI_thread_lock(LOCK_CUSTOM1); - } - - face_seam_flag = ps->faceSeamFlags[tri_index]; - - /* are any of our edges un-initialized? */ - if ((face_seam_flag & PROJ_FACE_SEAM_INIT0) == 0 || - (face_seam_flag & PROJ_FACE_SEAM_INIT1) == 0 || - (face_seam_flag & PROJ_FACE_SEAM_INIT2) == 0) - { - project_face_seams_init(ps, arena, tri_index, 0, true, ibuf->x, ibuf->y); - face_seam_flag = ps->faceSeamFlags[tri_index]; - //printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM0, flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2); - } - - if ((face_seam_flag & (PROJ_FACE_SEAM0 | PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2)) == 0) { - - if (threaded) { - /* Other threads could be modifying these vars. */ - BLI_thread_unlock(LOCK_CUSTOM1); - } - - } - else { - /* we have a seam - deal with it! */ - - /* inset face coords. NOTE!!! ScreenSace for ortho, Worldspace in perspective view */ - float insetCos[3][3]; - - /* vertex screenspace coords */ - const float *vCoSS[3]; - - /* Store the screenspace coords of the face, - * clipped by the bucket's screen aligned rectangle. */ - float bucket_clip_edges[2][2]; - float edge_verts_inset_clip[2][3]; - /* face edge pairs - loop throuh these: - * ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */ - int fidx1, fidx2; - - float seam_subsection[4][2]; - float fac1, fac2; - - /* Pixelspace UVs. */ - float lt_puv[3][2]; - - lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x; - lt_puv[0][1] = lt_uv_pxoffset[0][1] * ibuf->y; - - lt_puv[1][0] = lt_uv_pxoffset[1][0] * ibuf->x; - lt_puv[1][1] = lt_uv_pxoffset[1][1] * ibuf->y; - - lt_puv[2][0] = lt_uv_pxoffset[2][0] * ibuf->x; - lt_puv[2][1] = lt_uv_pxoffset[2][1] * ibuf->y; - - if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) || - (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) || - (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2)) - { - uv_image_outset(ps, lt_uv_pxoffset, lt_puv, tri_index, ibuf->x, ibuf->y); - } - - /* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */ - if (threaded) { - /* Other threads could be modifying these vars */ - BLI_thread_unlock(LOCK_CUSTOM1); - } - - vCoSS[0] = ps->screenCoords[lt_vtri[0]]; - vCoSS[1] = ps->screenCoords[lt_vtri[1]]; - vCoSS[2] = ps->screenCoords[lt_vtri[2]]; - - /* PROJ_FACE_SCALE_SEAM must be slightly less then 1.0f */ - if (is_ortho) { - scale_tri(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM); - } - else { - scale_tri(insetCos, vCo, PROJ_FACE_SCALE_SEAM); - } - - for (fidx1 = 0; fidx1 < 3; fidx1++) { - /* next fidx in the face (0,1,2) -> (1,2,0) */ - fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1; - - if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ - line_clip_rect2f(clip_rect, bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])) - { - /* Avoid div by zero. */ - if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) { - uint loop_idx = ps->mlooptri_eval[tri_index].tri[fidx1]; - LoopSeamData *seam_data = &ps->loopSeamData[loop_idx]; - float (*seam_uvs)[2] = seam_data->seam_uvs; - - if (is_ortho) { - fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]); - fac2 = line_point_factor_v2(bucket_clip_edges[1], vCoSS[fidx1], vCoSS[fidx2]); - } - else { - fac1 = screen_px_line_point_factor_v2_persp(ps, bucket_clip_edges[0], vCo[fidx1], vCo[fidx2]); - fac2 = screen_px_line_point_factor_v2_persp(ps, bucket_clip_edges[1], vCo[fidx1], vCo[fidx2]); - } - - interp_v2_v2v2(seam_subsection[0], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac1); - interp_v2_v2v2(seam_subsection[1], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac2); - - interp_v2_v2v2(seam_subsection[2], seam_uvs[0], seam_uvs[1], fac2); - interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], fac1); - - /* if the bucket_clip_edges values Z values was kept we could avoid this - * Inset needs to be added so occlusion tests wont hit adjacent faces */ - interp_v3_v3v3(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1); - interp_v3_v3v3(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2); - - - if (pixel_bounds_uv(seam_subsection, &bounds_px, ibuf->x, ibuf->y)) { - /* bounds between the seam rect and the uvspace bucket pixels */ - - has_isect = 0; - for (y = bounds_px.ymin; y < bounds_px.ymax; y++) { - // uv[1] = (((float)y) + 0.5f) / (float)ibuf->y; - /* use offset uvs instead */ - uv[1] = (float)y / ibuf_yf; - - has_x_isect = 0; - for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { - float puv[2] = {(float)x, (float)y}; - bool in_bounds; - //uv[0] = (((float)x) + 0.5f) / (float)ibuf->x; - /* use offset uvs instead */ - uv[0] = (float)x / ibuf_xf; - - /* test we're inside uvspace bucket and triangle bounds */ - if (equals_v2v2(seam_uvs[0], seam_uvs[1])) { - in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection)); - } - else { - in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection)); - } - - if (in_bounds) { - if ((seam_data->corner_dist_sq[0] > 0.0f) && - (len_squared_v2v2(puv, seam_data->seam_puvs[0]) < seam_data->corner_dist_sq[0]) && - (len_squared_v2v2(puv, lt_puv[fidx1]) > ps->seam_bleed_px_sq)) - { - in_bounds = false; - } - else if ((seam_data->corner_dist_sq[1] > 0.0f) && - (len_squared_v2v2(puv, seam_data->seam_puvs[1]) < seam_data->corner_dist_sq[1]) && - (len_squared_v2v2(puv, lt_puv[fidx2]) > ps->seam_bleed_px_sq)) - { - in_bounds = false; - } - } - - if (in_bounds) { - float pixel_on_edge[4]; - float fac; - - if (is_ortho) { - screen_px_from_ortho(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); - } - else { - screen_px_from_persp(uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); - } - - /* We need the coord of the pixel on the edge, for the occlusion query. */ - fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection)); - interp_v3_v3v3(pixel_on_edge, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); - - if (!is_ortho) { - pixel_on_edge[3] = 1.0f; - /* cast because of const */ - mul_m4_v4((float(*)[4])ps->projectMat, pixel_on_edge); - pixel_on_edge[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * pixel_on_edge[0] / pixel_on_edge[3]; - pixel_on_edge[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * pixel_on_edge[1] / pixel_on_edge[3]; - /* Use the depth for bucket point occlusion */ - pixel_on_edge[2] = pixel_on_edge[2] / pixel_on_edge[3]; - } - - if ((ps->do_occlude == false) || - !project_bucket_point_occluded(ps, bucketFaceNodes, tri_index, pixel_on_edge)) - { - /* a pity we need to get the worldspace - * pixel location here */ - if (do_clip || do_3d_mapping) { - interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w); - - if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { - /* Watch out that no code below - * this needs to run */ - continue; - } - } - - mask = project_paint_uvpixel_mask(ps, tri_index, w); - - if (mask > 0.0f) { - BLI_linklist_prepend_arena( - bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, &tinf, x, y, mask, tri_index, - pixelScreenCo, wco, w), - arena - ); - } - - } - } - else if (has_x_isect) { - /* assuming the face is not a bow-tie - we know - * we cant intersect again on the X */ - break; - } - } - -#if 0 /* TODO - investigate why this dosnt work sometimes! it should! */ - /* no intersection for this entire row, - * after some intersection above means we can quit now */ - if (has_x_isect == 0 && has_isect) { - break; - } -#endif - } - } - } - } - } - } - } + if (ps->seam_bleed_px > 0.0f && !(ps->faceSeamFlags[tri_index] & PROJ_FACE_DEGENERATE)) { + int face_seam_flag; + + if (threaded) { + /* Other threads could be modifying these vars. */ + BLI_thread_lock(LOCK_CUSTOM1); + } + + face_seam_flag = ps->faceSeamFlags[tri_index]; + + /* are any of our edges un-initialized? */ + if ((face_seam_flag & PROJ_FACE_SEAM_INIT0) == 0 || + (face_seam_flag & PROJ_FACE_SEAM_INIT1) == 0 || + (face_seam_flag & PROJ_FACE_SEAM_INIT2) == 0) { + project_face_seams_init(ps, arena, tri_index, 0, true, ibuf->x, ibuf->y); + face_seam_flag = ps->faceSeamFlags[tri_index]; + //printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM0, flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2); + } + + if ((face_seam_flag & (PROJ_FACE_SEAM0 | PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2)) == 0) { + + if (threaded) { + /* Other threads could be modifying these vars. */ + BLI_thread_unlock(LOCK_CUSTOM1); + } + } + else { + /* we have a seam - deal with it! */ + + /* inset face coords. NOTE!!! ScreenSace for ortho, Worldspace in perspective view */ + float insetCos[3][3]; + + /* vertex screenspace coords */ + const float *vCoSS[3]; + + /* Store the screenspace coords of the face, + * clipped by the bucket's screen aligned rectangle. */ + float bucket_clip_edges[2][2]; + float edge_verts_inset_clip[2][3]; + /* face edge pairs - loop throuh these: + * ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */ + int fidx1, fidx2; + + float seam_subsection[4][2]; + float fac1, fac2; + + /* Pixelspace UVs. */ + float lt_puv[3][2]; + + lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x; + lt_puv[0][1] = lt_uv_pxoffset[0][1] * ibuf->y; + + lt_puv[1][0] = lt_uv_pxoffset[1][0] * ibuf->x; + lt_puv[1][1] = lt_uv_pxoffset[1][1] * ibuf->y; + + lt_puv[2][0] = lt_uv_pxoffset[2][0] * ibuf->x; + lt_puv[2][1] = lt_uv_pxoffset[2][1] * ibuf->y; + + if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) || + (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) || + (ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2)) { + uv_image_outset(ps, lt_uv_pxoffset, lt_puv, tri_index, ibuf->x, ibuf->y); + } + + /* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */ + if (threaded) { + /* Other threads could be modifying these vars */ + BLI_thread_unlock(LOCK_CUSTOM1); + } + + vCoSS[0] = ps->screenCoords[lt_vtri[0]]; + vCoSS[1] = ps->screenCoords[lt_vtri[1]]; + vCoSS[2] = ps->screenCoords[lt_vtri[2]]; + + /* PROJ_FACE_SCALE_SEAM must be slightly less then 1.0f */ + if (is_ortho) { + scale_tri(insetCos, vCoSS, PROJ_FACE_SCALE_SEAM); + } + else { + scale_tri(insetCos, vCo, PROJ_FACE_SCALE_SEAM); + } + + for (fidx1 = 0; fidx1 < 3; fidx1++) { + /* next fidx in the face (0,1,2) -> (1,2,0) */ + fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1; + + if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ + line_clip_rect2f(clip_rect, + bucket_bounds, + vCoSS[fidx1], + vCoSS[fidx2], + bucket_clip_edges[0], + bucket_clip_edges[1])) { + /* Avoid div by zero. */ + if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) { + uint loop_idx = ps->mlooptri_eval[tri_index].tri[fidx1]; + LoopSeamData *seam_data = &ps->loopSeamData[loop_idx]; + float(*seam_uvs)[2] = seam_data->seam_uvs; + + if (is_ortho) { + fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]); + fac2 = line_point_factor_v2(bucket_clip_edges[1], vCoSS[fidx1], vCoSS[fidx2]); + } + else { + fac1 = screen_px_line_point_factor_v2_persp( + ps, bucket_clip_edges[0], vCo[fidx1], vCo[fidx2]); + fac2 = screen_px_line_point_factor_v2_persp( + ps, bucket_clip_edges[1], vCo[fidx1], vCo[fidx2]); + } + + interp_v2_v2v2(seam_subsection[0], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac1); + interp_v2_v2v2(seam_subsection[1], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac2); + + interp_v2_v2v2(seam_subsection[2], seam_uvs[0], seam_uvs[1], fac2); + interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], fac1); + + /* if the bucket_clip_edges values Z values was kept we could avoid this + * Inset needs to be added so occlusion tests wont hit adjacent faces */ + interp_v3_v3v3(edge_verts_inset_clip[0], insetCos[fidx1], insetCos[fidx2], fac1); + interp_v3_v3v3(edge_verts_inset_clip[1], insetCos[fidx1], insetCos[fidx2], fac2); + + if (pixel_bounds_uv(seam_subsection, &bounds_px, ibuf->x, ibuf->y)) { + /* bounds between the seam rect and the uvspace bucket pixels */ + + has_isect = 0; + for (y = bounds_px.ymin; y < bounds_px.ymax; y++) { + // uv[1] = (((float)y) + 0.5f) / (float)ibuf->y; + /* use offset uvs instead */ + uv[1] = (float)y / ibuf_yf; + + has_x_isect = 0; + for (x = bounds_px.xmin; x < bounds_px.xmax; x++) { + float puv[2] = {(float)x, (float)y}; + bool in_bounds; + //uv[0] = (((float)x) + 0.5f) / (float)ibuf->x; + /* use offset uvs instead */ + uv[0] = (float)x / ibuf_xf; + + /* test we're inside uvspace bucket and triangle bounds */ + if (equals_v2v2(seam_uvs[0], seam_uvs[1])) { + in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection)); + } + else { + in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection)); + } + + if (in_bounds) { + if ((seam_data->corner_dist_sq[0] > 0.0f) && + (len_squared_v2v2(puv, seam_data->seam_puvs[0]) < + seam_data->corner_dist_sq[0]) && + (len_squared_v2v2(puv, lt_puv[fidx1]) > ps->seam_bleed_px_sq)) { + in_bounds = false; + } + else if ((seam_data->corner_dist_sq[1] > 0.0f) && + (len_squared_v2v2(puv, seam_data->seam_puvs[1]) < + seam_data->corner_dist_sq[1]) && + (len_squared_v2v2(puv, lt_puv[fidx2]) > ps->seam_bleed_px_sq)) { + in_bounds = false; + } + } + + if (in_bounds) { + float pixel_on_edge[4]; + float fac; + + if (is_ortho) { + screen_px_from_ortho( + uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); + } + else { + screen_px_from_persp( + uv, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, pixelScreenCo, w); + } + + /* We need the coord of the pixel on the edge, for the occlusion query. */ + fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection)); + interp_v3_v3v3( + pixel_on_edge, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); + + if (!is_ortho) { + pixel_on_edge[3] = 1.0f; + /* cast because of const */ + mul_m4_v4((float(*)[4])ps->projectMat, pixel_on_edge); + pixel_on_edge[0] = (float)(ps->winx * 0.5f) + + (ps->winx * 0.5f) * pixel_on_edge[0] / pixel_on_edge[3]; + pixel_on_edge[1] = (float)(ps->winy * 0.5f) + + (ps->winy * 0.5f) * pixel_on_edge[1] / pixel_on_edge[3]; + /* Use the depth for bucket point occlusion */ + pixel_on_edge[2] = pixel_on_edge[2] / pixel_on_edge[3]; + } + + if ((ps->do_occlude == false) || + !project_bucket_point_occluded( + ps, bucketFaceNodes, tri_index, pixel_on_edge)) { + /* a pity we need to get the worldspace + * pixel location here */ + if (do_clip || do_3d_mapping) { + interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w); + + if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { + /* Watch out that no code below + * this needs to run */ + continue; + } + } + + mask = project_paint_uvpixel_mask(ps, tri_index, w); + + if (mask > 0.0f) { + BLI_linklist_prepend_arena( + bucketPixelNodes, + project_paint_uvpixel_init( + ps, arena, &tinf, x, y, mask, tri_index, pixelScreenCo, wco, w), + arena); + } + } + } + else if (has_x_isect) { + /* assuming the face is not a bow-tie - we know + * we cant intersect again on the X */ + break; + } + } + +# if 0 /* TODO - investigate why this dosnt work sometimes! it should! */ + /* no intersection for this entire row, + * after some intersection above means we can quit now */ + if (has_x_isect == 0 && has_isect) { + break; + } +# endif + } + } + } + } + } + } + } #else - UNUSED_VARS(vCo, threaded); -#endif // PROJ_DEBUG_NOSEAMBLEED + UNUSED_VARS(vCo, threaded); +#endif // PROJ_DEBUG_NOSEAMBLEED } - /** * Takes floating point screenspace min/max and * returns int min/max to be used as indices for ps->bucketRect, ps->bucketFlags */ -static void project_paint_bucket_bounds( - const ProjPaintState *ps, - const float min[2], const float max[2], - int bucketMin[2], int bucketMax[2]) +static void project_paint_bucket_bounds(const ProjPaintState *ps, + const float min[2], + const float max[2], + int bucketMin[2], + int bucketMax[2]) { - /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */ - - /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f - * is always truncated to 1, is this really correct?? - jwilkins */ - - /* these offsets of 0.5 and 1.5 seem odd but they are correct */ - bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); - bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f); - - bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f); - bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f); - - /* in case the rect is outside the mesh 2d bounds */ - CLAMP(bucketMin[0], 0, ps->buckets_x); - CLAMP(bucketMin[1], 0, ps->buckets_y); - - CLAMP(bucketMax[0], 0, ps->buckets_x); - CLAMP(bucketMax[1], 0, ps->buckets_y); + /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */ + + /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f + * is always truncated to 1, is this really correct?? - jwilkins */ + + /* these offsets of 0.5 and 1.5 seem odd but they are correct */ + bucketMin[0] = + (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); + bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * + ps->buckets_y) + + 0.5f); + + bucketMax[0] = + (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f); + bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * + ps->buckets_y) + + 1.5f); + + /* in case the rect is outside the mesh 2d bounds */ + CLAMP(bucketMin[0], 0, ps->buckets_x); + CLAMP(bucketMin[1], 0, ps->buckets_y); + + CLAMP(bucketMax[0], 0, ps->buckets_x); + CLAMP(bucketMax[1], 0, ps->buckets_y); } /* set bucket_bounds to a screen space-aligned floating point bound-box */ -static void project_bucket_bounds( - const ProjPaintState *ps, const int bucket_x, const int bucket_y, rctf *bucket_bounds) +static void project_bucket_bounds(const ProjPaintState *ps, + const int bucket_x, + const int bucket_y, + rctf *bucket_bounds) { - /* left */ - bucket_bounds->xmin = ( - ps->screenMin[0] + ((bucket_x) * (ps->screen_width / ps->buckets_x))); - /* right */ - bucket_bounds->xmax = ( - ps->screenMin[0] + ((bucket_x + 1) * (ps->screen_width / ps->buckets_x))); - - /* bottom */ - bucket_bounds->ymin = ( - ps->screenMin[1] + ((bucket_y) * (ps->screen_height / ps->buckets_y))); - /* top */ - bucket_bounds->ymax = ( - ps->screenMin[1] + ((bucket_y + 1) * (ps->screen_height / ps->buckets_y))); + /* left */ + bucket_bounds->xmin = (ps->screenMin[0] + ((bucket_x) * (ps->screen_width / ps->buckets_x))); + /* right */ + bucket_bounds->xmax = (ps->screenMin[0] + ((bucket_x + 1) * (ps->screen_width / ps->buckets_x))); + + /* bottom */ + bucket_bounds->ymin = (ps->screenMin[1] + ((bucket_y) * (ps->screen_height / ps->buckets_y))); + /* top */ + bucket_bounds->ymax = (ps->screenMin[1] + + ((bucket_y + 1) * (ps->screen_height / ps->buckets_y))); } /* Fill this bucket with pixels from the faces that intersect it. * * have bucket_bounds as an argument so we don't need to give bucket_x/y the rect function needs */ -static void project_bucket_init( - const ProjPaintState *ps, const int thread_index, const int bucket_index, - const rctf *clip_rect, const rctf *bucket_bounds) +static void project_bucket_init(const ProjPaintState *ps, + const int thread_index, + const int bucket_index, + const rctf *clip_rect, + const rctf *bucket_bounds) { - LinkNode *node; - int tri_index, image_index = 0; - ImBuf *ibuf = NULL; - Image *tpage_last = NULL, *tpage; - ImBuf *tmpibuf = NULL; - - if (ps->image_tot == 1) { - /* Simple loop, no context switching */ - ibuf = ps->projImages[0].ibuf; - - for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - project_paint_face_init( - ps, thread_index, bucket_index, POINTER_AS_INT(node->link), 0, - clip_rect, bucket_bounds, ibuf, &tmpibuf); - } - } - else { - - /* More complicated loop, switch between images */ - for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - tri_index = POINTER_AS_INT(node->link); - - /* Image context switching */ - tpage = project_paint_face_paint_image(ps, tri_index); - if (tpage_last != tpage) { - tpage_last = tpage; - - for (image_index = 0; image_index < ps->image_tot; image_index++) { - if (ps->projImages[image_index].ima == tpage_last) { - ibuf = ps->projImages[image_index].ibuf; - break; - } - } - } - /* context switching done */ - - project_paint_face_init( - ps, thread_index, bucket_index, tri_index, image_index, - clip_rect, bucket_bounds, ibuf, &tmpibuf); - } - } - - if (tmpibuf) - IMB_freeImBuf(tmpibuf); - - ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT; + LinkNode *node; + int tri_index, image_index = 0; + ImBuf *ibuf = NULL; + Image *tpage_last = NULL, *tpage; + ImBuf *tmpibuf = NULL; + + if (ps->image_tot == 1) { + /* Simple loop, no context switching */ + ibuf = ps->projImages[0].ibuf; + + for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { + project_paint_face_init(ps, + thread_index, + bucket_index, + POINTER_AS_INT(node->link), + 0, + clip_rect, + bucket_bounds, + ibuf, + &tmpibuf); + } + } + else { + + /* More complicated loop, switch between images */ + for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { + tri_index = POINTER_AS_INT(node->link); + + /* Image context switching */ + tpage = project_paint_face_paint_image(ps, tri_index); + if (tpage_last != tpage) { + tpage_last = tpage; + + for (image_index = 0; image_index < ps->image_tot; image_index++) { + if (ps->projImages[image_index].ima == tpage_last) { + ibuf = ps->projImages[image_index].ibuf; + break; + } + } + } + /* context switching done */ + + project_paint_face_init(ps, + thread_index, + bucket_index, + tri_index, + image_index, + clip_rect, + bucket_bounds, + ibuf, + &tmpibuf); + } + } + + if (tmpibuf) + IMB_freeImBuf(tmpibuf); + + ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT; } - /* We want to know if a bucket and a face overlap in screen-space * * Note, if this ever returns false positives its not that bad, since a face in the bounding area * will have its pixels calculated when it might not be needed later, (at the moment at least) * obviously it shouldn't have bugs though */ -static bool project_bucket_face_isect(ProjPaintState *ps, int bucket_x, int bucket_y, const MLoopTri *lt) +static bool project_bucket_face_isect(ProjPaintState *ps, + int bucket_x, + int bucket_y, + const MLoopTri *lt) { - /* TODO - replace this with a tricker method that uses sideofline for all - * screenCoords's edges against the closest bucket corner */ - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - rctf bucket_bounds; - float p1[2], p2[2], p3[2], p4[2]; - const float *v, *v1, *v2, *v3; - int fidx; - - project_bucket_bounds(ps, bucket_x, bucket_y, &bucket_bounds); - - /* Is one of the faces verts in the bucket bounds? */ - - fidx = 2; - do { - v = ps->screenCoords[lt_vtri[fidx]]; - if (BLI_rctf_isect_pt_v(&bucket_bounds, v)) { - return 1; - } - } while (fidx--); - - v1 = ps->screenCoords[lt_vtri[0]]; - v2 = ps->screenCoords[lt_vtri[1]]; - v3 = ps->screenCoords[lt_vtri[2]]; - - p1[0] = bucket_bounds.xmin; p1[1] = bucket_bounds.ymin; - p2[0] = bucket_bounds.xmin; p2[1] = bucket_bounds.ymax; - p3[0] = bucket_bounds.xmax; p3[1] = bucket_bounds.ymax; - p4[0] = bucket_bounds.xmax; p4[1] = bucket_bounds.ymin; - - if (isect_point_tri_v2(p1, v1, v2, v3) || - isect_point_tri_v2(p2, v1, v2, v3) || - isect_point_tri_v2(p3, v1, v2, v3) || - isect_point_tri_v2(p4, v1, v2, v3) || - /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */ - (isect_seg_seg_v2(p1, p2, v1, v2) || isect_seg_seg_v2(p1, p2, v2, v3)) || - (isect_seg_seg_v2(p2, p3, v1, v2) || isect_seg_seg_v2(p2, p3, v2, v3)) || - (isect_seg_seg_v2(p3, p4, v1, v2) || isect_seg_seg_v2(p3, p4, v2, v3)) || - (isect_seg_seg_v2(p4, p1, v1, v2) || isect_seg_seg_v2(p4, p1, v2, v3))) - { - return 1; - } - - return 0; + /* TODO - replace this with a tricker method that uses sideofline for all + * screenCoords's edges against the closest bucket corner */ + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + rctf bucket_bounds; + float p1[2], p2[2], p3[2], p4[2]; + const float *v, *v1, *v2, *v3; + int fidx; + + project_bucket_bounds(ps, bucket_x, bucket_y, &bucket_bounds); + + /* Is one of the faces verts in the bucket bounds? */ + + fidx = 2; + do { + v = ps->screenCoords[lt_vtri[fidx]]; + if (BLI_rctf_isect_pt_v(&bucket_bounds, v)) { + return 1; + } + } while (fidx--); + + v1 = ps->screenCoords[lt_vtri[0]]; + v2 = ps->screenCoords[lt_vtri[1]]; + v3 = ps->screenCoords[lt_vtri[2]]; + + p1[0] = bucket_bounds.xmin; + p1[1] = bucket_bounds.ymin; + p2[0] = bucket_bounds.xmin; + p2[1] = bucket_bounds.ymax; + p3[0] = bucket_bounds.xmax; + p3[1] = bucket_bounds.ymax; + p4[0] = bucket_bounds.xmax; + p4[1] = bucket_bounds.ymin; + + if (isect_point_tri_v2(p1, v1, v2, v3) || isect_point_tri_v2(p2, v1, v2, v3) || + isect_point_tri_v2(p3, v1, v2, v3) || isect_point_tri_v2(p4, v1, v2, v3) || + /* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */ + (isect_seg_seg_v2(p1, p2, v1, v2) || isect_seg_seg_v2(p1, p2, v2, v3)) || + (isect_seg_seg_v2(p2, p3, v1, v2) || isect_seg_seg_v2(p2, p3, v2, v3)) || + (isect_seg_seg_v2(p3, p4, v1, v2) || isect_seg_seg_v2(p3, p4, v2, v3)) || + (isect_seg_seg_v2(p4, p1, v1, v2) || isect_seg_seg_v2(p4, p1, v2, v3))) { + return 1; + } + + return 0; } /* Add faces to the bucket but don't initialize its pixels * TODO - when painting occluded, sort the faces on their min-Z * and only add faces that faces that are not occluded */ -static void project_paint_delayed_face_init(ProjPaintState *ps, const MLoopTri *lt, const int tri_index) +static void project_paint_delayed_face_init(ProjPaintState *ps, + const MLoopTri *lt, + const int tri_index) { - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - float min[2], max[2], *vCoSS; - /* for ps->bucketRect indexing */ - int bucketMin[2], bucketMax[2]; - int fidx, bucket_x, bucket_y; - /* for early loop exit */ - int has_x_isect = -1, has_isect = 0; - /* just use the first thread arena since threading has not started yet */ - MemArena *arena = ps->arena_mt[0]; - - INIT_MINMAX2(min, max); - - fidx = 2; - do { - vCoSS = ps->screenCoords[lt_vtri[fidx]]; - minmax_v2v2_v2(min, max, vCoSS); - } while (fidx--); - - project_paint_bucket_bounds(ps, min, max, bucketMin, bucketMax); - - for (bucket_y = bucketMin[1]; bucket_y < bucketMax[1]; bucket_y++) { - has_x_isect = 0; - for (bucket_x = bucketMin[0]; bucket_x < bucketMax[0]; bucket_x++) { - if (project_bucket_face_isect(ps, bucket_x, bucket_y, lt)) { - int bucket_index = bucket_x + (bucket_y * ps->buckets_x); - BLI_linklist_prepend_arena( - &ps->bucketFaces[bucket_index], - /* cast to a pointer to shut up the compiler */ - POINTER_FROM_INT(tri_index), - arena - ); - - has_x_isect = has_isect = 1; - } - else if (has_x_isect) { - /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ - break; - } - } - - /* no intersection for this entire row, - * after some intersection above means we can quit now */ - if (has_x_isect == 0 && has_isect) { - break; - } - } + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + float min[2], max[2], *vCoSS; + /* for ps->bucketRect indexing */ + int bucketMin[2], bucketMax[2]; + int fidx, bucket_x, bucket_y; + /* for early loop exit */ + int has_x_isect = -1, has_isect = 0; + /* just use the first thread arena since threading has not started yet */ + MemArena *arena = ps->arena_mt[0]; + + INIT_MINMAX2(min, max); + + fidx = 2; + do { + vCoSS = ps->screenCoords[lt_vtri[fidx]]; + minmax_v2v2_v2(min, max, vCoSS); + } while (fidx--); + + project_paint_bucket_bounds(ps, min, max, bucketMin, bucketMax); + + for (bucket_y = bucketMin[1]; bucket_y < bucketMax[1]; bucket_y++) { + has_x_isect = 0; + for (bucket_x = bucketMin[0]; bucket_x < bucketMax[0]; bucket_x++) { + if (project_bucket_face_isect(ps, bucket_x, bucket_y, lt)) { + int bucket_index = bucket_x + (bucket_y * ps->buckets_x); + BLI_linklist_prepend_arena(&ps->bucketFaces[bucket_index], + /* cast to a pointer to shut up the compiler */ + POINTER_FROM_INT(tri_index), + arena); + + has_x_isect = has_isect = 1; + } + else if (has_x_isect) { + /* assuming the face is not a bow-tie - we know we cant intersect again on the X */ + break; + } + } + + /* no intersection for this entire row, + * after some intersection above means we can quit now */ + if (has_x_isect == 0 && has_isect) { + break; + } + } #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->seam_bleed_px > 0.0f) { - /* set as uninitialized */ - ps->loopSeamData[lt->tri[0]].seam_uvs[0][0] = FLT_MAX; - ps->loopSeamData[lt->tri[1]].seam_uvs[0][0] = FLT_MAX; - ps->loopSeamData[lt->tri[2]].seam_uvs[0][0] = FLT_MAX; - } + if (ps->seam_bleed_px > 0.0f) { + /* set as uninitialized */ + ps->loopSeamData[lt->tri[0]].seam_uvs[0][0] = FLT_MAX; + ps->loopSeamData[lt->tri[1]].seam_uvs[0][0] = FLT_MAX; + ps->loopSeamData[lt->tri[2]].seam_uvs[0][0] = FLT_MAX; + } #endif } static void proj_paint_state_viewport_init(ProjPaintState *ps, const char symmetry_flag) { - float mat[3][3]; - float viewmat[4][4]; - float viewinv[4][4]; - - ps->viewDir[0] = 0.0f; - ps->viewDir[1] = 0.0f; - ps->viewDir[2] = 1.0f; - - copy_m4_m4(ps->obmat, ps->ob->obmat); - - if (symmetry_flag) { - int i; - for (i = 0; i < 3; i++) { - if ((symmetry_flag >> i) & 1) { - negate_v3(ps->obmat[i]); - ps->is_flip_object = !ps->is_flip_object; - } - } - } - - invert_m4_m4(ps->obmat_imat, ps->obmat); - - if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { - /* normal drawing */ - ps->winx = ps->ar->winx; - ps->winy = ps->ar->winy; - - copy_m4_m4(viewmat, ps->rv3d->viewmat); - copy_m4_m4(viewinv, ps->rv3d->viewinv); - - ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat); - - ps->is_ortho = ED_view3d_clip_range_get(ps->depsgraph, ps->v3d, ps->rv3d, &ps->clip_start, &ps->clip_end, true); - } - else { - /* re-projection */ - float winmat[4][4]; - float vmat[4][4]; - - ps->winx = ps->reproject_ibuf->x; - ps->winy = ps->reproject_ibuf->y; - - if (ps->source == PROJ_SRC_IMAGE_VIEW) { - /* image stores camera data, tricky */ - IDProperty *idgroup = IDP_GetProperties(&ps->reproject_image->id, 0); - IDProperty *view_data = IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID); - - const float *array = (float *)IDP_Array(view_data); - - /* use image array, written when creating image */ - memcpy(winmat, array, sizeof(winmat)); array += sizeof(winmat) / sizeof(float); - memcpy(viewmat, array, sizeof(viewmat)); array += sizeof(viewmat) / sizeof(float); - ps->clip_start = array[0]; - ps->clip_end = array[1]; - ps->is_ortho = array[2] ? 1 : 0; - - invert_m4_m4(viewinv, viewmat); - } - else if (ps->source == PROJ_SRC_IMAGE_CAM) { - Object *cam_ob_eval = DEG_get_evaluated_object(ps->depsgraph, ps->scene->camera); - CameraParams params; - - /* viewmat & viewinv */ - copy_m4_m4(viewinv, cam_ob_eval->obmat); - normalize_m4(viewinv); - invert_m4_m4(viewmat, viewinv); - - /* window matrix, clipping and ortho */ - BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, cam_ob_eval); - BKE_camera_params_compute_viewplane(¶ms, ps->winx, ps->winy, 1.0f, 1.0f); - BKE_camera_params_compute_matrix(¶ms); - - copy_m4_m4(winmat, params.winmat); - ps->clip_start = params.clip_start; - ps->clip_end = params.clip_end; - ps->is_ortho = params.is_ortho; - } - else { - BLI_assert(0); - } - - /* same as #ED_view3d_ob_project_mat_get */ - mul_m4_m4m4(vmat, viewmat, ps->obmat); - mul_m4_m4m4(ps->projectMat, winmat, vmat); - } - - invert_m4_m4(ps->projectMatInv, ps->projectMat); - - /* viewDir - object relative */ - copy_m3_m4(mat, viewinv); - mul_m3_v3(mat, ps->viewDir); - copy_m3_m4(mat, ps->obmat_imat); - mul_m3_v3(mat, ps->viewDir); - normalize_v3(ps->viewDir); - - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(ps->viewDir); - } - - /* viewPos - object relative */ - copy_v3_v3(ps->viewPos, viewinv[3]); - copy_m3_m4(mat, ps->obmat_imat); - mul_m3_v3(mat, ps->viewPos); - add_v3_v3(ps->viewPos, ps->obmat_imat[3]); + float mat[3][3]; + float viewmat[4][4]; + float viewinv[4][4]; + + ps->viewDir[0] = 0.0f; + ps->viewDir[1] = 0.0f; + ps->viewDir[2] = 1.0f; + + copy_m4_m4(ps->obmat, ps->ob->obmat); + + if (symmetry_flag) { + int i; + for (i = 0; i < 3; i++) { + if ((symmetry_flag >> i) & 1) { + negate_v3(ps->obmat[i]); + ps->is_flip_object = !ps->is_flip_object; + } + } + } + + invert_m4_m4(ps->obmat_imat, ps->obmat); + + if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { + /* normal drawing */ + ps->winx = ps->ar->winx; + ps->winy = ps->ar->winy; + + copy_m4_m4(viewmat, ps->rv3d->viewmat); + copy_m4_m4(viewinv, ps->rv3d->viewinv); + + ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat); + + ps->is_ortho = ED_view3d_clip_range_get( + ps->depsgraph, ps->v3d, ps->rv3d, &ps->clip_start, &ps->clip_end, true); + } + else { + /* re-projection */ + float winmat[4][4]; + float vmat[4][4]; + + ps->winx = ps->reproject_ibuf->x; + ps->winy = ps->reproject_ibuf->y; + + if (ps->source == PROJ_SRC_IMAGE_VIEW) { + /* image stores camera data, tricky */ + IDProperty *idgroup = IDP_GetProperties(&ps->reproject_image->id, 0); + IDProperty *view_data = IDP_GetPropertyFromGroup(idgroup, PROJ_VIEW_DATA_ID); + + const float *array = (float *)IDP_Array(view_data); + + /* use image array, written when creating image */ + memcpy(winmat, array, sizeof(winmat)); + array += sizeof(winmat) / sizeof(float); + memcpy(viewmat, array, sizeof(viewmat)); + array += sizeof(viewmat) / sizeof(float); + ps->clip_start = array[0]; + ps->clip_end = array[1]; + ps->is_ortho = array[2] ? 1 : 0; + + invert_m4_m4(viewinv, viewmat); + } + else if (ps->source == PROJ_SRC_IMAGE_CAM) { + Object *cam_ob_eval = DEG_get_evaluated_object(ps->depsgraph, ps->scene->camera); + CameraParams params; + + /* viewmat & viewinv */ + copy_m4_m4(viewinv, cam_ob_eval->obmat); + normalize_m4(viewinv); + invert_m4_m4(viewmat, viewinv); + + /* window matrix, clipping and ortho */ + BKE_camera_params_init(¶ms); + BKE_camera_params_from_object(¶ms, cam_ob_eval); + BKE_camera_params_compute_viewplane(¶ms, ps->winx, ps->winy, 1.0f, 1.0f); + BKE_camera_params_compute_matrix(¶ms); + + copy_m4_m4(winmat, params.winmat); + ps->clip_start = params.clip_start; + ps->clip_end = params.clip_end; + ps->is_ortho = params.is_ortho; + } + else { + BLI_assert(0); + } + + /* same as #ED_view3d_ob_project_mat_get */ + mul_m4_m4m4(vmat, viewmat, ps->obmat); + mul_m4_m4m4(ps->projectMat, winmat, vmat); + } + + invert_m4_m4(ps->projectMatInv, ps->projectMat); + + /* viewDir - object relative */ + copy_m3_m4(mat, viewinv); + mul_m3_v3(mat, ps->viewDir); + copy_m3_m4(mat, ps->obmat_imat); + mul_m3_v3(mat, ps->viewDir); + normalize_v3(ps->viewDir); + + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(ps->viewDir); + } + + /* viewPos - object relative */ + copy_v3_v3(ps->viewPos, viewinv[3]); + copy_m3_m4(mat, ps->obmat_imat); + mul_m3_v3(mat, ps->viewPos); + add_v3_v3(ps->viewPos, ps->obmat_imat[3]); } static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter) { - const MVert *mv; - float *projScreenCo; - float projMargin; - int a; - - INIT_MINMAX2(ps->screenMin, ps->screenMax); - - ps->screenCoords = MEM_mallocN(sizeof(float) * ps->totvert_eval * 4, "ProjectPaint ScreenVerts"); - projScreenCo = *ps->screenCoords; - - if (ps->is_ortho) { - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { - mul_v3_m4v3(projScreenCo, ps->projectMat, mv->co); - - /* screen space, not clamped */ - projScreenCo[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0]; - projScreenCo[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1]; - minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); - } - } - else { - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { - copy_v3_v3(projScreenCo, mv->co); - projScreenCo[3] = 1.0f; - - mul_m4_v4(ps->projectMat, projScreenCo); - - if (projScreenCo[3] > ps->clip_start) { - /* screen space, not clamped */ - projScreenCo[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0] / projScreenCo[3]; - projScreenCo[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1] / projScreenCo[3]; - /* Use the depth for bucket point occlusion */ - projScreenCo[2] = projScreenCo[2] / projScreenCo[3]; - minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); - } - else { - /* TODO - deal with cases where 1 side of a face goes behind the view ? - * - * After some research this is actually very tricky, only option is to - * clip the derived mesh before painting, which is a Pain */ - projScreenCo[0] = FLT_MAX; - } - } - } - - /* If this border is not added we get artifacts for faces that - * have a parallel edge and at the bounds of the 2D projected verts eg - * - a single screen aligned quad */ - projMargin = (ps->screenMax[0] - ps->screenMin[0]) * 0.000001f; - ps->screenMax[0] += projMargin; - ps->screenMin[0] -= projMargin; - projMargin = (ps->screenMax[1] - ps->screenMin[1]) * 0.000001f; - ps->screenMax[1] += projMargin; - ps->screenMin[1] -= projMargin; - - if (ps->source == PROJ_SRC_VIEW) { + const MVert *mv; + float *projScreenCo; + float projMargin; + int a; + + INIT_MINMAX2(ps->screenMin, ps->screenMax); + + ps->screenCoords = MEM_mallocN(sizeof(float) * ps->totvert_eval * 4, "ProjectPaint ScreenVerts"); + projScreenCo = *ps->screenCoords; + + if (ps->is_ortho) { + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { + mul_v3_m4v3(projScreenCo, ps->projectMat, mv->co); + + /* screen space, not clamped */ + projScreenCo[0] = (float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projScreenCo[0]; + projScreenCo[1] = (float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projScreenCo[1]; + minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); + } + } + else { + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++, projScreenCo += 4) { + copy_v3_v3(projScreenCo, mv->co); + projScreenCo[3] = 1.0f; + + mul_m4_v4(ps->projectMat, projScreenCo); + + if (projScreenCo[3] > ps->clip_start) { + /* screen space, not clamped */ + projScreenCo[0] = (float)(ps->winx * 0.5f) + + (ps->winx * 0.5f) * projScreenCo[0] / projScreenCo[3]; + projScreenCo[1] = (float)(ps->winy * 0.5f) + + (ps->winy * 0.5f) * projScreenCo[1] / projScreenCo[3]; + /* Use the depth for bucket point occlusion */ + projScreenCo[2] = projScreenCo[2] / projScreenCo[3]; + minmax_v2v2_v2(ps->screenMin, ps->screenMax, projScreenCo); + } + else { + /* TODO - deal with cases where 1 side of a face goes behind the view ? + * + * After some research this is actually very tricky, only option is to + * clip the derived mesh before painting, which is a Pain */ + projScreenCo[0] = FLT_MAX; + } + } + } + + /* If this border is not added we get artifacts for faces that + * have a parallel edge and at the bounds of the 2D projected verts eg + * - a single screen aligned quad */ + projMargin = (ps->screenMax[0] - ps->screenMin[0]) * 0.000001f; + ps->screenMax[0] += projMargin; + ps->screenMin[0] -= projMargin; + projMargin = (ps->screenMax[1] - ps->screenMin[1]) * 0.000001f; + ps->screenMax[1] += projMargin; + ps->screenMin[1] -= projMargin; + + if (ps->source == PROJ_SRC_VIEW) { #ifdef PROJ_DEBUG_WINCLIP - CLAMP(ps->screenMin[0], (float)(-diameter), (float)(ps->winx + diameter)); - CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter)); + CLAMP(ps->screenMin[0], (float)(-diameter), (float)(ps->winx + diameter)); + CLAMP(ps->screenMax[0], (float)(-diameter), (float)(ps->winx + diameter)); - CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter)); - CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); + CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter)); + CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); #else - UNUSED_VARS(diameter); + UNUSED_VARS(diameter); #endif - } - else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */ - ps->screenMin[0] = 0; - ps->screenMax[0] = (float)(ps->winx); - - ps->screenMin[1] = 0; - ps->screenMax[1] = (float)(ps->winy); - } + } + else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */ + ps->screenMin[0] = 0; + ps->screenMax[0] = (float)(ps->winx); + + ps->screenMin[1] = 0; + ps->screenMax[1] = (float)(ps->winy); + } } static void proj_paint_state_cavity_init(ProjPaintState *ps) { - const MVert *mv; - const MEdge *me; - float *cavities; - int a; - - if (ps->do_mask_cavity) { - int *counter = MEM_callocN(sizeof(int) * ps->totvert_eval, "counter"); - float (*edges)[3] = MEM_callocN(sizeof(float) * 3 * ps->totvert_eval, "edges"); - ps->cavities = MEM_mallocN(sizeof(float) * ps->totvert_eval, "ProjectPaint Cavities"); - cavities = ps->cavities; - - for (a = 0, me = ps->medge_eval; a < ps->totedge_eval; a++, me++) { - float e[3]; - sub_v3_v3v3(e, ps->mvert_eval[me->v1].co, ps->mvert_eval[me->v2].co); - normalize_v3(e); - add_v3_v3(edges[me->v2], e); - counter[me->v2]++; - sub_v3_v3(edges[me->v1], e); - counter[me->v1]++; - } - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { - if (counter[a] > 0) { - float no[3]; - mul_v3_fl(edges[a], 1.0f / counter[a]); - normal_short_to_float_v3(no, mv->no); - /* augment the diffe*/ - cavities[a] = saacos(10.0f * dot_v3v3(no, edges[a])) * (float)M_1_PI; - } - else - cavities[a] = 0.0; - } - - MEM_freeN(counter); - MEM_freeN(edges); - } + const MVert *mv; + const MEdge *me; + float *cavities; + int a; + + if (ps->do_mask_cavity) { + int *counter = MEM_callocN(sizeof(int) * ps->totvert_eval, "counter"); + float(*edges)[3] = MEM_callocN(sizeof(float) * 3 * ps->totvert_eval, "edges"); + ps->cavities = MEM_mallocN(sizeof(float) * ps->totvert_eval, "ProjectPaint Cavities"); + cavities = ps->cavities; + + for (a = 0, me = ps->medge_eval; a < ps->totedge_eval; a++, me++) { + float e[3]; + sub_v3_v3v3(e, ps->mvert_eval[me->v1].co, ps->mvert_eval[me->v2].co); + normalize_v3(e); + add_v3_v3(edges[me->v2], e); + counter[me->v2]++; + sub_v3_v3(edges[me->v1], e); + counter[me->v1]++; + } + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { + if (counter[a] > 0) { + float no[3]; + mul_v3_fl(edges[a], 1.0f / counter[a]); + normal_short_to_float_v3(no, mv->no); + /* augment the diffe*/ + cavities[a] = saacos(10.0f * dot_v3v3(no, edges[a])) * (float)M_1_PI; + } + else + cavities[a] = 0.0; + } + + MEM_freeN(counter); + MEM_freeN(edges); + } } #ifndef PROJ_DEBUG_NOSEAMBLEED static void proj_paint_state_seam_bleed_init(ProjPaintState *ps) { - if (ps->seam_bleed_px > 0.0f) { - ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->totvert_eval, "paint-vertFaces"); - ps->faceSeamFlags = MEM_callocN(sizeof(ushort) * ps->totlooptri_eval, "paint-faceSeamFlags"); - ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->totlooptri_eval, "paint-faceWindindFlags"); - ps->loopSeamData = MEM_mallocN(sizeof(LoopSeamData) * ps->totloop_eval, "paint-loopSeamUVs"); - ps->vertSeams = MEM_callocN(sizeof(ListBase) * ps->totvert_eval, "paint-vertSeams"); - } + if (ps->seam_bleed_px > 0.0f) { + ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->totvert_eval, "paint-vertFaces"); + ps->faceSeamFlags = MEM_callocN(sizeof(ushort) * ps->totlooptri_eval, "paint-faceSeamFlags"); + ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->totlooptri_eval, + "paint-faceWindindFlags"); + ps->loopSeamData = MEM_mallocN(sizeof(LoopSeamData) * ps->totloop_eval, "paint-loopSeamUVs"); + ps->vertSeams = MEM_callocN(sizeof(ListBase) * ps->totvert_eval, "paint-vertSeams"); + } } #endif static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_threads) { - int a; + int a; - /* Thread stuff - * - * very small brushes run a lot slower multithreaded since the advantage with - * threads is being able to fill in multiple buckets at once. - * Only use threads for bigger brushes. */ + /* Thread stuff + * + * very small brushes run a lot slower multithreaded since the advantage with + * threads is being able to fill in multiple buckets at once. + * Only use threads for bigger brushes. */ - ps->thread_tot = BKE_scene_num_threads(ps->scene); + ps->thread_tot = BKE_scene_num_threads(ps->scene); - /* workaround for #35057, disable threading if diameter is less than is possible for - * optimum bucket number generation */ - if (reset_threads) - ps->thread_tot = 1; + /* workaround for #35057, disable threading if diameter is less than is possible for + * optimum bucket number generation */ + if (reset_threads) + ps->thread_tot = 1; - if (ps->is_shared_user == false) { - if (ps->thread_tot > 1) { - ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); - BLI_spin_init(ps->tile_lock); - } + if (ps->is_shared_user == false) { + if (ps->thread_tot > 1) { + ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); + BLI_spin_init(ps->tile_lock); + } - image_undo_init_locks(); - } + image_undo_init_locks(); + } - for (a = 0; a < ps->thread_tot; a++) { - ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); - } + for (a = 0; a < ps->thread_tot; a++) { + ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); + } } static void proj_paint_state_vert_flags_init(ProjPaintState *ps) { - if (ps->do_backfacecull && ps->do_mask_normal) { - float viewDirPersp[3]; - const MVert *mv; - float no[3]; - int a; - - ps->vertFlags = MEM_callocN(sizeof(char) * ps->totvert_eval, "paint-vertFlags"); - - for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { - normal_short_to_float_v3(no, mv->no); - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(no); - } - - if (ps->is_ortho) { - if (dot_v3v3(ps->viewDir, no) <= ps->normal_angle__cos) { - /* 1 vert of this face is towards us */ - ps->vertFlags[a] |= PROJ_VERT_CULL; - } - } - else { - sub_v3_v3v3(viewDirPersp, ps->viewPos, mv->co); - normalize_v3(viewDirPersp); - if (UNLIKELY(ps->is_flip_object)) { - negate_v3(viewDirPersp); - } - if (dot_v3v3(viewDirPersp, no) <= ps->normal_angle__cos) { - /* 1 vert of this face is towards us */ - ps->vertFlags[a] |= PROJ_VERT_CULL; - } - } - } - } - else { - ps->vertFlags = NULL; - } + if (ps->do_backfacecull && ps->do_mask_normal) { + float viewDirPersp[3]; + const MVert *mv; + float no[3]; + int a; + + ps->vertFlags = MEM_callocN(sizeof(char) * ps->totvert_eval, "paint-vertFlags"); + + for (a = 0, mv = ps->mvert_eval; a < ps->totvert_eval; a++, mv++) { + normal_short_to_float_v3(no, mv->no); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } + + if (ps->is_ortho) { + if (dot_v3v3(ps->viewDir, no) <= ps->normal_angle__cos) { + /* 1 vert of this face is towards us */ + ps->vertFlags[a] |= PROJ_VERT_CULL; + } + } + else { + sub_v3_v3v3(viewDirPersp, ps->viewPos, mv->co); + normalize_v3(viewDirPersp); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(viewDirPersp); + } + if (dot_v3v3(viewDirPersp, no) <= ps->normal_angle__cos) { + /* 1 vert of this face is towards us */ + ps->vertFlags[a] |= PROJ_VERT_CULL; + } + } + } + } + else { + ps->vertFlags = NULL; + } } #ifndef PROJ_DEBUG_NOSEAMBLEED -static void project_paint_bleed_add_face_user( - const ProjPaintState *ps, MemArena *arena, - const MLoopTri *lt, const int tri_index) +static void project_paint_bleed_add_face_user(const ProjPaintState *ps, + MemArena *arena, + const MLoopTri *lt, + const int tri_index) { - /* add face user if we have bleed enabled, set the UV seam flags later */ - /* annoying but we need to add all faces even ones we never use elsewhere */ - if (ps->seam_bleed_px > 0.0f) { - const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; - - /* Check for degenerate triangles. Degenerate faces cause trouble with bleed computations. - * Ideally this would be checked later, not to add to the cost of computing non-degenerate - * triangles, but that would allow other triangles to still find adjacent seams on degenerate - * triangles, potentially causing incorrect results. */ - if (area_tri_v2(UNPACK3(lt_tri_uv)) > FLT_EPSILON) { - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - void *tri_index_p = POINTER_FROM_INT(tri_index); - - BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[0]], tri_index_p, arena); - BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[1]], tri_index_p, arena); - BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[2]], tri_index_p, arena); - } - else { - ps->faceSeamFlags[tri_index] |= PROJ_FACE_DEGENERATE; - } - } + /* add face user if we have bleed enabled, set the UV seam flags later */ + /* annoying but we need to add all faces even ones we never use elsewhere */ + if (ps->seam_bleed_px > 0.0f) { + const float *lt_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt)}; + + /* Check for degenerate triangles. Degenerate faces cause trouble with bleed computations. + * Ideally this would be checked later, not to add to the cost of computing non-degenerate + * triangles, but that would allow other triangles to still find adjacent seams on degenerate + * triangles, potentially causing incorrect results. */ + if (area_tri_v2(UNPACK3(lt_tri_uv)) > FLT_EPSILON) { + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + void *tri_index_p = POINTER_FROM_INT(tri_index); + + BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[0]], tri_index_p, arena); + BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[1]], tri_index_p, arena); + BLI_linklist_prepend_arena(&ps->vertFaces[lt_vtri[2]], tri_index_p, arena); + } + else { + ps->faceSeamFlags[tri_index] |= PROJ_FACE_DEGENERATE; + } + } } #endif /* Return true if evaluated mesh can be painted on, false otherwise */ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *ps) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = ps->ob; - - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - - if (scene_eval == NULL || ob_eval == NULL) { - return false; - } - - CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask; - cddata_masks.fmask |= CD_MASK_MTFACE; - cddata_masks.lmask |= CD_MASK_MLOOPUV; - - /* Workaround for subsurf selection, try the display mesh first */ - if (ps->source == PROJ_SRC_IMAGE_CAM) { - /* using render mesh, assume only camera was rendered from */ - ps->me_eval = mesh_create_eval_final_render( - depsgraph, scene_eval, ob_eval, &cddata_masks); - ps->me_eval_free = true; - } - else { - if (ps->do_face_sel) { - cddata_masks.vmask |= CD_MASK_ORIGINDEX; - cddata_masks.emask |= CD_MASK_ORIGINDEX; - cddata_masks.pmask |= CD_MASK_ORIGINDEX; - } - ps->me_eval = mesh_get_eval_final( - depsgraph, scene_eval, ob_eval, - &cddata_masks); - ps->me_eval_free = false; - } - - if (!CustomData_has_layer(&ps->me_eval->ldata, CD_MLOOPUV)) { - if (ps->me_eval_free) { - BKE_id_free(NULL, ps->me_eval); - } - ps->me_eval = NULL; - return false; - } - - /* Build final material array, we use this a lot here. */ - /* materials start from 1, default material is 0 */ - const int totmat = ob->totcol + 1; - ps->mat_array = MEM_malloc_arrayN(totmat, sizeof(*ps->mat_array), __func__); - /* We leave last material as empty - rationale here is being able to index - * the materials by using the mf->mat_nr directly and leaving the last - * material as NULL in case no materials exist on mesh, so indexing will not fail. */ - for (int i = 0; i < totmat - 1; i++) { - ps->mat_array[i] = give_current_material(ob, i + 1); - } - ps->mat_array[totmat - 1] = NULL; - - ps->mvert_eval = ps->me_eval->mvert; - if (ps->do_mask_cavity) { - ps->medge_eval = ps->me_eval->medge; - } - ps->mloop_eval = ps->me_eval->mloop; - ps->mpoly_eval = ps->me_eval->mpoly; - - ps->totvert_eval = ps->me_eval->totvert; - ps->totedge_eval = ps->me_eval->totedge; - ps->totpoly_eval = ps->me_eval->totpoly; - ps->totloop_eval = ps->me_eval->totloop; - - ps->mlooptri_eval = BKE_mesh_runtime_looptri_ensure(ps->me_eval); - ps->totlooptri_eval = ps->me_eval->runtime.looptris.len; - - ps->poly_to_loop_uv = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); - - return true; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = ps->ob; + + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + if (scene_eval == NULL || ob_eval == NULL) { + return false; + } + + CustomData_MeshMasks cddata_masks = scene_eval->customdata_mask; + cddata_masks.fmask |= CD_MASK_MTFACE; + cddata_masks.lmask |= CD_MASK_MLOOPUV; + + /* Workaround for subsurf selection, try the display mesh first */ + if (ps->source == PROJ_SRC_IMAGE_CAM) { + /* using render mesh, assume only camera was rendered from */ + ps->me_eval = mesh_create_eval_final_render(depsgraph, scene_eval, ob_eval, &cddata_masks); + ps->me_eval_free = true; + } + else { + if (ps->do_face_sel) { + cddata_masks.vmask |= CD_MASK_ORIGINDEX; + cddata_masks.emask |= CD_MASK_ORIGINDEX; + cddata_masks.pmask |= CD_MASK_ORIGINDEX; + } + ps->me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); + ps->me_eval_free = false; + } + + if (!CustomData_has_layer(&ps->me_eval->ldata, CD_MLOOPUV)) { + if (ps->me_eval_free) { + BKE_id_free(NULL, ps->me_eval); + } + ps->me_eval = NULL; + return false; + } + + /* Build final material array, we use this a lot here. */ + /* materials start from 1, default material is 0 */ + const int totmat = ob->totcol + 1; + ps->mat_array = MEM_malloc_arrayN(totmat, sizeof(*ps->mat_array), __func__); + /* We leave last material as empty - rationale here is being able to index + * the materials by using the mf->mat_nr directly and leaving the last + * material as NULL in case no materials exist on mesh, so indexing will not fail. */ + for (int i = 0; i < totmat - 1; i++) { + ps->mat_array[i] = give_current_material(ob, i + 1); + } + ps->mat_array[totmat - 1] = NULL; + + ps->mvert_eval = ps->me_eval->mvert; + if (ps->do_mask_cavity) { + ps->medge_eval = ps->me_eval->medge; + } + ps->mloop_eval = ps->me_eval->mloop; + ps->mpoly_eval = ps->me_eval->mpoly; + + ps->totvert_eval = ps->me_eval->totvert; + ps->totedge_eval = ps->me_eval->totedge; + ps->totpoly_eval = ps->me_eval->totpoly; + ps->totloop_eval = ps->me_eval->totloop; + + ps->mlooptri_eval = BKE_mesh_runtime_looptri_ensure(ps->me_eval); + ps->totlooptri_eval = ps->me_eval->runtime.looptris.len; + + ps->poly_to_loop_uv = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); + + return true; } typedef struct { - const MLoopUV *mloopuv_clone_base; - const TexPaintSlot *slot_last_clone; - const TexPaintSlot *slot_clone; + const MLoopUV *mloopuv_clone_base; + const TexPaintSlot *slot_last_clone; + const TexPaintSlot *slot_clone; } ProjPaintLayerClone; -static void proj_paint_layer_clone_init( - ProjPaintState *ps, - ProjPaintLayerClone *layer_clone) +static void proj_paint_layer_clone_init(ProjPaintState *ps, ProjPaintLayerClone *layer_clone) { - MLoopUV *mloopuv_clone_base = NULL; + MLoopUV *mloopuv_clone_base = NULL; - /* use clone mtface? */ - if (ps->do_layer_clone) { - const int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); + /* use clone mtface? */ + if (ps->do_layer_clone) { + const int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); - ps->poly_to_loop_uv_clone = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), "proj_paint_mtfaces"); + ps->poly_to_loop_uv_clone = MEM_mallocN(ps->totpoly_eval * sizeof(MLoopUV *), + "proj_paint_mtfaces"); - if (layer_num != -1) - mloopuv_clone_base = CustomData_get_layer_n(&ps->me_eval->ldata, CD_MLOOPUV, layer_num); + if (layer_num != -1) + mloopuv_clone_base = CustomData_get_layer_n(&ps->me_eval->ldata, CD_MLOOPUV, layer_num); - if (mloopuv_clone_base == NULL) { - /* get active instead */ - mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - } + if (mloopuv_clone_base == NULL) { + /* get active instead */ + mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + } + } - } - - memset(layer_clone, 0, sizeof(*layer_clone)); - layer_clone->mloopuv_clone_base = mloopuv_clone_base; + memset(layer_clone, 0, sizeof(*layer_clone)); + layer_clone->mloopuv_clone_base = mloopuv_clone_base; } /* Return true if face should be skipped, false otherwise */ -static bool project_paint_clone_face_skip( - ProjPaintState *ps, - ProjPaintLayerClone *lc, - const TexPaintSlot *slot, - const int tri_index) +static bool project_paint_clone_face_skip(ProjPaintState *ps, + ProjPaintLayerClone *lc, + const TexPaintSlot *slot, + const int tri_index) { - if (ps->do_layer_clone) { - if (ps->do_material_slots) { - lc->slot_clone = project_paint_face_clone_slot(ps, tri_index); - /* all faces should have a valid slot, reassert here */ - if (ELEM(lc->slot_clone, NULL, slot)) - return true; - } - else if (ps->clone_ima == ps->canvas_ima) - return true; - - if (ps->do_material_slots) { - if (lc->slot_clone != lc->slot_last_clone) { - if (!slot->uvname || - !(lc->mloopuv_clone_base = CustomData_get_layer_named( - &ps->me_eval->ldata, CD_MLOOPUV, - lc->slot_clone->uvname))) - { - lc->mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - } - lc->slot_last_clone = lc->slot_clone; - } - } - - /* will set multiple times for 4+ sided poly */ - ps->poly_to_loop_uv_clone[ps->mlooptri_eval[tri_index].poly] = lc->mloopuv_clone_base; - } - return false; + if (ps->do_layer_clone) { + if (ps->do_material_slots) { + lc->slot_clone = project_paint_face_clone_slot(ps, tri_index); + /* all faces should have a valid slot, reassert here */ + if (ELEM(lc->slot_clone, NULL, slot)) + return true; + } + else if (ps->clone_ima == ps->canvas_ima) + return true; + + if (ps->do_material_slots) { + if (lc->slot_clone != lc->slot_last_clone) { + if (!slot->uvname || !(lc->mloopuv_clone_base = CustomData_get_layer_named( + &ps->me_eval->ldata, CD_MLOOPUV, lc->slot_clone->uvname))) { + lc->mloopuv_clone_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + } + lc->slot_last_clone = lc->slot_clone; + } + } + + /* will set multiple times for 4+ sided poly */ + ps->poly_to_loop_uv_clone[ps->mlooptri_eval[tri_index].poly] = lc->mloopuv_clone_base; + } + return false; } typedef struct { - const MPoly *mpoly_orig; + const MPoly *mpoly_orig; - const int *index_mp_to_orig; + const int *index_mp_to_orig; } ProjPaintFaceLookup; -static void proj_paint_face_lookup_init( - const ProjPaintState *ps, - ProjPaintFaceLookup *face_lookup) +static void proj_paint_face_lookup_init(const ProjPaintState *ps, ProjPaintFaceLookup *face_lookup) { - memset(face_lookup, 0, sizeof(*face_lookup)); - if (ps->do_face_sel) { - face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX); - face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; - } + memset(face_lookup, 0, sizeof(*face_lookup)); + if (ps->do_face_sel) { + face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX); + face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly; + } } /* Return true if face should be considered selected, false otherwise */ -static bool project_paint_check_face_sel( - const ProjPaintState *ps, - const ProjPaintFaceLookup *face_lookup, - const MLoopTri *lt) +static bool project_paint_check_face_sel(const ProjPaintState *ps, + const ProjPaintFaceLookup *face_lookup, + const MLoopTri *lt) { - if (ps->do_face_sel) { - int orig_index; - const MPoly *mp; - - if ((face_lookup->index_mp_to_orig != NULL) && - (((orig_index = (face_lookup->index_mp_to_orig[lt->poly]))) != ORIGINDEX_NONE)) - { - mp = &face_lookup->mpoly_orig[orig_index]; - } - else { - mp = &ps->mpoly_eval[lt->poly]; - } - - return ((mp->flag & ME_FACE_SEL) != 0); - } - else { - return true; - } + if (ps->do_face_sel) { + int orig_index; + const MPoly *mp; + + if ((face_lookup->index_mp_to_orig != NULL) && + (((orig_index = (face_lookup->index_mp_to_orig[lt->poly]))) != ORIGINDEX_NONE)) { + mp = &face_lookup->mpoly_orig[orig_index]; + } + else { + mp = &ps->mpoly_eval[lt->poly]; + } + + return ((mp->flag & ME_FACE_SEL) != 0); + } + else { + return true; + } } typedef struct { - const float *v1; - const float *v2; - const float *v3; + const float *v1; + const float *v2; + const float *v3; } ProjPaintFaceCoSS; -static void proj_paint_face_coSS_init( - const ProjPaintState *ps, const MLoopTri *lt, - ProjPaintFaceCoSS *coSS) +static void proj_paint_face_coSS_init(const ProjPaintState *ps, + const MLoopTri *lt, + ProjPaintFaceCoSS *coSS) { - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - coSS->v1 = ps->screenCoords[lt_vtri[0]]; - coSS->v2 = ps->screenCoords[lt_vtri[1]]; - coSS->v3 = ps->screenCoords[lt_vtri[2]]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + coSS->v1 = ps->screenCoords[lt_vtri[0]]; + coSS->v2 = ps->screenCoords[lt_vtri[1]]; + coSS->v3 = ps->screenCoords[lt_vtri[2]]; } /* Return true if face should be culled, false otherwise */ -static bool project_paint_flt_max_cull( - const ProjPaintState *ps, - const ProjPaintFaceCoSS *coSS) +static bool project_paint_flt_max_cull(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS) { - if (!ps->is_ortho) { - if (coSS->v1[0] == FLT_MAX || - coSS->v2[0] == FLT_MAX || - coSS->v3[0] == FLT_MAX) - { - return true; - } - } - return false; + if (!ps->is_ortho) { + if (coSS->v1[0] == FLT_MAX || coSS->v2[0] == FLT_MAX || coSS->v3[0] == FLT_MAX) { + return true; + } + } + return false; } #ifdef PROJ_DEBUG_WINCLIP /* Return true if face should be culled, false otherwise */ -static bool project_paint_winclip( - const ProjPaintState *ps, - const ProjPaintFaceCoSS *coSS) +static bool project_paint_winclip(const ProjPaintState *ps, const ProjPaintFaceCoSS *coSS) { - /* ignore faces outside the view */ - return ((ps->source != PROJ_SRC_VIEW_FILL) && - ((coSS->v1[0] < ps->screenMin[0] && - coSS->v2[0] < ps->screenMin[0] && - coSS->v3[0] < ps->screenMin[0]) || - - (coSS->v1[0] > ps->screenMax[0] && - coSS->v2[0] > ps->screenMax[0] && - coSS->v3[0] > ps->screenMax[0]) || - - (coSS->v1[1] < ps->screenMin[1] && - coSS->v2[1] < ps->screenMin[1] && - coSS->v3[1] < ps->screenMin[1]) || - - (coSS->v1[1] > ps->screenMax[1] && - coSS->v2[1] > ps->screenMax[1] && - coSS->v3[1] > ps->screenMax[1]))); -} -#endif //PROJ_DEBUG_WINCLIP + /* ignore faces outside the view */ + return ((ps->source != PROJ_SRC_VIEW_FILL) && + ((coSS->v1[0] < ps->screenMin[0] && coSS->v2[0] < ps->screenMin[0] && + coSS->v3[0] < ps->screenMin[0]) || + (coSS->v1[0] > ps->screenMax[0] && coSS->v2[0] > ps->screenMax[0] && + coSS->v3[0] > ps->screenMax[0]) || -static void project_paint_build_proj_ima( - ProjPaintState *ps, MemArena *arena, - LinkNode *image_LinkList) + (coSS->v1[1] < ps->screenMin[1] && coSS->v2[1] < ps->screenMin[1] && + coSS->v3[1] < ps->screenMin[1]) || + + (coSS->v1[1] > ps->screenMax[1] && coSS->v2[1] > ps->screenMax[1] && + coSS->v3[1] > ps->screenMax[1]))); +} +#endif //PROJ_DEBUG_WINCLIP + +static void project_paint_build_proj_ima(ProjPaintState *ps, + MemArena *arena, + LinkNode *image_LinkList) { - ProjPaintImage *projIma; - LinkNode *node; - int i; - - /* build an array of images we use */ - projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); - - for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { - int size; - projIma->ima = node->link; - projIma->touch = 0; - projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); - size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y); - projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); - partial_redraw_array_init(projIma->partRedrawRect); - projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); - memset((void *)projIma->undoRect, 0, size); - projIma->maskRect = BLI_memarena_alloc(arena, size); - memset(projIma->maskRect, 0, size); - projIma->valid = BLI_memarena_alloc(arena, size); - memset(projIma->valid, 0, size); - } + ProjPaintImage *projIma; + LinkNode *node; + int i; + + /* build an array of images we use */ + projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); + + for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + int size; + projIma->ima = node->link; + projIma->touch = 0; + projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); + size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * + IMAPAINT_TILE_NUMBER(projIma->ibuf->y); + projIma->partRedrawRect = BLI_memarena_alloc( + arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + partial_redraw_array_init(projIma->partRedrawRect); + projIma->undoRect = (volatile void **)BLI_memarena_alloc(arena, size); + memset((void *)projIma->undoRect, 0, size); + projIma->maskRect = BLI_memarena_alloc(arena, size); + memset(projIma->maskRect, 0, size); + projIma->valid = BLI_memarena_alloc(arena, size); + memset(projIma->valid, 0, size); + } } -static void project_paint_prepare_all_faces( - ProjPaintState *ps, MemArena *arena, - const ProjPaintFaceLookup *face_lookup, - ProjPaintLayerClone *layer_clone, - const MLoopUV *mloopuv_base, - const bool is_multi_view) +static void project_paint_prepare_all_faces(ProjPaintState *ps, + MemArena *arena, + const ProjPaintFaceLookup *face_lookup, + ProjPaintLayerClone *layer_clone, + const MLoopUV *mloopuv_base, + const bool is_multi_view) { - /* Image Vars - keep track of images we have used */ - LinkNodePair image_LinkList = {NULL, NULL}; - - Image *tpage_last = NULL, *tpage; - TexPaintSlot *slot_last = NULL; - TexPaintSlot *slot = NULL; - const MLoopTri *lt; - int image_index = -1, tri_index; - int prev_poly = -1; - - for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) { - bool is_face_sel; - bool skip_tri = false; - - is_face_sel = project_paint_check_face_sel(ps, face_lookup, lt); - - if (!ps->do_stencil_brush) { - slot = project_paint_face_paint_slot(ps, tri_index); - /* all faces should have a valid slot, reassert here */ - if (slot == NULL) { - mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - tpage = ps->canvas_ima; - } - else { - if (slot != slot_last) { - if (!slot->uvname || !(mloopuv_base = CustomData_get_layer_named(&ps->me_eval->ldata, CD_MLOOPUV, slot->uvname))) - mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - slot_last = slot; - } - - /* don't allow using the same inage for painting and stencilling */ - if (slot->ima == ps->stencil_ima) { - /* Delay continuing the loop until after loop_uvs and bleed faces are initialized. - * While this shouldn't be used, face-winding reads all polys. - * It's less trouble to set all faces to valid UV's, - * avoiding NULL checks all over. */ - skip_tri = true; - } - else { - tpage = slot->ima; - } - } - } - else { - tpage = ps->stencil_ima; - } - - ps->poly_to_loop_uv[lt->poly] = mloopuv_base; + /* Image Vars - keep track of images we have used */ + LinkNodePair image_LinkList = {NULL, NULL}; + + Image *tpage_last = NULL, *tpage; + TexPaintSlot *slot_last = NULL; + TexPaintSlot *slot = NULL; + const MLoopTri *lt; + int image_index = -1, tri_index; + int prev_poly = -1; + + for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) { + bool is_face_sel; + bool skip_tri = false; + + is_face_sel = project_paint_check_face_sel(ps, face_lookup, lt); + + if (!ps->do_stencil_brush) { + slot = project_paint_face_paint_slot(ps, tri_index); + /* all faces should have a valid slot, reassert here */ + if (slot == NULL) { + mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + tpage = ps->canvas_ima; + } + else { + if (slot != slot_last) { + if (!slot->uvname || !(mloopuv_base = CustomData_get_layer_named( + &ps->me_eval->ldata, CD_MLOOPUV, slot->uvname))) + mloopuv_base = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + slot_last = slot; + } + + /* don't allow using the same inage for painting and stencilling */ + if (slot->ima == ps->stencil_ima) { + /* Delay continuing the loop until after loop_uvs and bleed faces are initialized. + * While this shouldn't be used, face-winding reads all polys. + * It's less trouble to set all faces to valid UV's, + * avoiding NULL checks all over. */ + skip_tri = true; + } + else { + tpage = slot->ima; + } + } + } + else { + tpage = ps->stencil_ima; + } + + ps->poly_to_loop_uv[lt->poly] = mloopuv_base; #ifndef PROJ_DEBUG_NOSEAMBLEED - project_paint_bleed_add_face_user(ps, arena, lt, tri_index); + project_paint_bleed_add_face_user(ps, arena, lt, tri_index); #endif - if (skip_tri || project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) { - continue; - } + if (skip_tri || project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) { + continue; + } - /* tfbase here should be non-null! */ - BLI_assert(mloopuv_base != NULL); + /* tfbase here should be non-null! */ + BLI_assert(mloopuv_base != NULL); - if (is_face_sel && tpage) { - ProjPaintFaceCoSS coSS; - proj_paint_face_coSS_init(ps, lt, &coSS); + if (is_face_sel && tpage) { + ProjPaintFaceCoSS coSS; + proj_paint_face_coSS_init(ps, lt, &coSS); - if (is_multi_view == false) { - if (project_paint_flt_max_cull(ps, &coSS)) { - continue; - } + if (is_multi_view == false) { + if (project_paint_flt_max_cull(ps, &coSS)) { + continue; + } #ifdef PROJ_DEBUG_WINCLIP - if (project_paint_winclip(ps, &coSS)) { - continue; - } - -#endif //PROJ_DEBUG_WINCLIP - - /* backface culls individual triangles but mask normal will use polygon */ - if (ps->do_backfacecull) { - if (ps->do_mask_normal) { - if (prev_poly != lt->poly) { - int iloop; - bool culled = true; - const MPoly *poly = ps->mpoly_eval + lt->poly; - int poly_loops = poly->totloop; - prev_poly = lt->poly; - for (iloop = 0; iloop < poly_loops; iloop++) { - if (!(ps->vertFlags[ps->mloop_eval[poly->loopstart + iloop].v] & PROJ_VERT_CULL)) { - culled = false; - break; - } - } - - if (culled) { - /* poly loops - 2 is number of triangles for poly, - * but counter gets incremented when continuing, so decrease by 3 */ - int poly_tri = poly_loops - 3; - tri_index += poly_tri; - lt += poly_tri; - continue; - } - } - } - else { - if ((line_point_side_v2(coSS.v1, coSS.v2, coSS.v3) < 0.0f) != ps->is_flip_object) { - continue; - } - } - } - } - - if (tpage_last != tpage) { - - image_index = BLI_linklist_index(image_LinkList.list, tpage); - - if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { - /* MemArena dosnt have an append func */ - BLI_linklist_append(&image_LinkList, tpage); - image_index = ps->image_tot; - ps->image_tot++; - } - - tpage_last = tpage; - } - - if (image_index != -1) { - /* Initialize the faces screen pixels */ - /* Add this to a list to initialize later */ - project_paint_delayed_face_init(ps, lt, tri_index); - } - } - } - - /* build an array of images we use*/ - if (ps->is_shared_user == false) { - project_paint_build_proj_ima(ps, arena, image_LinkList.list); - } - - /* we have built the array, discard the linked list */ - BLI_linklist_free(image_LinkList.list, NULL); + if (project_paint_winclip(ps, &coSS)) { + continue; + } + +#endif //PROJ_DEBUG_WINCLIP + + /* backface culls individual triangles but mask normal will use polygon */ + if (ps->do_backfacecull) { + if (ps->do_mask_normal) { + if (prev_poly != lt->poly) { + int iloop; + bool culled = true; + const MPoly *poly = ps->mpoly_eval + lt->poly; + int poly_loops = poly->totloop; + prev_poly = lt->poly; + for (iloop = 0; iloop < poly_loops; iloop++) { + if (!(ps->vertFlags[ps->mloop_eval[poly->loopstart + iloop].v] & PROJ_VERT_CULL)) { + culled = false; + break; + } + } + + if (culled) { + /* poly loops - 2 is number of triangles for poly, + * but counter gets incremented when continuing, so decrease by 3 */ + int poly_tri = poly_loops - 3; + tri_index += poly_tri; + lt += poly_tri; + continue; + } + } + } + else { + if ((line_point_side_v2(coSS.v1, coSS.v2, coSS.v3) < 0.0f) != ps->is_flip_object) { + continue; + } + } + } + } + + if (tpage_last != tpage) { + + image_index = BLI_linklist_index(image_LinkList.list, tpage); + + if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { + /* MemArena dosnt have an append func */ + BLI_linklist_append(&image_LinkList, tpage); + image_index = ps->image_tot; + ps->image_tot++; + } + + tpage_last = tpage; + } + + if (image_index != -1) { + /* Initialize the faces screen pixels */ + /* Add this to a list to initialize later */ + project_paint_delayed_face_init(ps, lt, tri_index); + } + } + } + + /* build an array of images we use*/ + if (ps->is_shared_user == false) { + project_paint_build_proj_ima(ps, arena, image_LinkList.list); + } + + /* we have built the array, discard the linked list */ + BLI_linklist_free(image_LinkList.list, NULL); } /* run once per stroke before projection painting */ -static void project_paint_begin( - const bContext *C, ProjPaintState *ps, - const bool is_multi_view, const char symmetry_flag) +static void project_paint_begin(const bContext *C, + ProjPaintState *ps, + const bool is_multi_view, + const char symmetry_flag) { - ProjPaintLayerClone layer_clone; - ProjPaintFaceLookup face_lookup; - const MLoopUV *mloopuv_base = NULL; + ProjPaintLayerClone layer_clone; + ProjPaintFaceLookup face_lookup; + const MLoopUV *mloopuv_base = NULL; - /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ - MemArena *arena; + /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ + MemArena *arena; - const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); - bool reset_threads = false; + bool reset_threads = false; - /* ---- end defines ---- */ + /* ---- end defines ---- */ - if (ps->source == PROJ_SRC_VIEW) { - /* faster clipping lookups */ - ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); - } + if (ps->source == PROJ_SRC_VIEW) { + /* faster clipping lookups */ + ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); + } - ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); - ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0; + ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); + ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0; - /* paint onto the derived mesh */ - if (ps->is_shared_user == false) { - if (!proj_paint_state_mesh_eval_init(C, ps)) { - return; - } - } + /* paint onto the derived mesh */ + if (ps->is_shared_user == false) { + if (!proj_paint_state_mesh_eval_init(C, ps)) { + return; + } + } - proj_paint_face_lookup_init(ps, &face_lookup); - proj_paint_layer_clone_init(ps, &layer_clone); + proj_paint_face_lookup_init(ps, &face_lookup); + proj_paint_layer_clone_init(ps, &layer_clone); - if (ps->do_layer_stencil || ps->do_stencil_brush) { - //int layer_num = CustomData_get_stencil_layer(&ps->me_eval->ldata, CD_MLOOPUV); - int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); - if (layer_num != -1) - ps->mloopuv_stencil_eval = CustomData_get_layer_n(&ps->me_eval->ldata, CD_MLOOPUV, layer_num); + if (ps->do_layer_stencil || ps->do_stencil_brush) { + //int layer_num = CustomData_get_stencil_layer(&ps->me_eval->ldata, CD_MLOOPUV); + int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->ldata, CD_MLOOPUV); + if (layer_num != -1) + ps->mloopuv_stencil_eval = CustomData_get_layer_n( + &ps->me_eval->ldata, CD_MLOOPUV, layer_num); - if (ps->mloopuv_stencil_eval == NULL) { - /* get active instead */ - ps->mloopuv_stencil_eval = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); - } + if (ps->mloopuv_stencil_eval == NULL) { + /* get active instead */ + ps->mloopuv_stencil_eval = CustomData_get_layer(&ps->me_eval->ldata, CD_MLOOPUV); + } - if (ps->do_stencil_brush) - mloopuv_base = ps->mloopuv_stencil_eval; - } + if (ps->do_stencil_brush) + mloopuv_base = ps->mloopuv_stencil_eval; + } - /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ - if (ps->is_shared_user == false) { - proj_paint_state_cavity_init(ps); - } + /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ + if (ps->is_shared_user == false) { + proj_paint_state_cavity_init(ps); + } - proj_paint_state_viewport_init(ps, symmetry_flag); + proj_paint_state_viewport_init(ps, symmetry_flag); - /* calculate vert screen coords - * run this early so we can calculate the x/y resolution of our bucket rect */ - proj_paint_state_screen_coords_init(ps, diameter); + /* calculate vert screen coords + * run this early so we can calculate the x/y resolution of our bucket rect */ + proj_paint_state_screen_coords_init(ps, diameter); - /* only for convenience */ - ps->screen_width = ps->screenMax[0] - ps->screenMin[0]; - ps->screen_height = ps->screenMax[1] - ps->screenMin[1]; + /* only for convenience */ + ps->screen_width = ps->screenMax[0] - ps->screenMin[0]; + ps->screen_height = ps->screenMax[1] - ps->screenMin[1]; - 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)); + 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; - } + if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { + reset_threads = true; + } - /* really high values could cause problems since it has to allocate a few - * (ps->buckets_x*ps->buckets_y) sized arrays */ - CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); - CLAMP(ps->buckets_y, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); + /* really high values could cause problems since it has to allocate a few + * (ps->buckets_x*ps->buckets_y) sized arrays */ + CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); + CLAMP(ps->buckets_y, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); - ps->bucketRect = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketRect"); - ps->bucketFaces = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); + ps->bucketRect = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, + "paint-bucketRect"); + ps->bucketFaces = MEM_callocN(sizeof(LinkNode *) * ps->buckets_x * ps->buckets_y, + "paint-bucketFaces"); - ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); + ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->is_shared_user == false) { - proj_paint_state_seam_bleed_init(ps); - } + if (ps->is_shared_user == false) { + proj_paint_state_seam_bleed_init(ps); + } #endif - proj_paint_state_thread_init(ps, reset_threads); - arena = ps->arena_mt[0]; + proj_paint_state_thread_init(ps, reset_threads); + arena = ps->arena_mt[0]; - proj_paint_state_vert_flags_init(ps); + proj_paint_state_vert_flags_init(ps); - project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, mloopuv_base, is_multi_view); + project_paint_prepare_all_faces( + ps, arena, &face_lookup, &layer_clone, mloopuv_base, is_multi_view); } static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) { - /* setup clone offset */ - if (ps->tool == PAINT_TOOL_CLONE) { - float projCo[4]; - copy_v3_v3(projCo, ps->scene->cursor.location); - mul_m4_v3(ps->obmat_imat, projCo); - - projCo[3] = 1.0f; - mul_m4_v4(ps->projectMat, projCo); - ps->cloneOffset[0] = mouse[0] - ((float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projCo[0] / projCo[3]); - ps->cloneOffset[1] = mouse[1] - ((float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projCo[1] / projCo[3]); - } + /* setup clone offset */ + if (ps->tool == PAINT_TOOL_CLONE) { + float projCo[4]; + copy_v3_v3(projCo, ps->scene->cursor.location); + mul_m4_v3(ps->obmat_imat, projCo); + + projCo[3] = 1.0f; + mul_m4_v4(ps->projectMat, projCo); + ps->cloneOffset[0] = mouse[0] - + ((float)(ps->winx * 0.5f) + (ps->winx * 0.5f) * projCo[0] / projCo[3]); + ps->cloneOffset[1] = mouse[1] - + ((float)(ps->winy * 0.5f) + (ps->winy * 0.5f) * projCo[1] / projCo[3]); + } } static void project_paint_end(ProjPaintState *ps) { - int a; - - image_undo_remove_masks(); - - /* dereference used image buffers */ - if (ps->is_shared_user == false) { - ProjPaintImage *projIma; - for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { - BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); - DEG_id_tag_update(&projIma->ima->id, 0); - } - } - - if (ps->reproject_ibuf_free_float) { - imb_freerectfloatImBuf(ps->reproject_ibuf); - } - if (ps->reproject_ibuf_free_uchar) { - imb_freerectImBuf(ps->reproject_ibuf); - } - BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); - - MEM_freeN(ps->screenCoords); - MEM_freeN(ps->bucketRect); - MEM_freeN(ps->bucketFaces); - MEM_freeN(ps->bucketFlags); - - if (ps->is_shared_user == false) { - if (ps->mat_array != NULL) { - MEM_freeN(ps->mat_array); - } - - /* must be set for non-shared */ - BLI_assert(ps->poly_to_loop_uv || ps->is_shared_user); - if (ps->poly_to_loop_uv) - MEM_freeN((void *)ps->poly_to_loop_uv); - - if (ps->do_layer_clone) - MEM_freeN((void *)ps->poly_to_loop_uv_clone); - if (ps->thread_tot > 1) { - BLI_spin_end(ps->tile_lock); - MEM_freeN((void *)ps->tile_lock); - } - - image_undo_end_locks(); + int a; + + image_undo_remove_masks(); + + /* dereference used image buffers */ + if (ps->is_shared_user == false) { + ProjPaintImage *projIma; + for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { + BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); + DEG_id_tag_update(&projIma->ima->id, 0); + } + } + + if (ps->reproject_ibuf_free_float) { + imb_freerectfloatImBuf(ps->reproject_ibuf); + } + if (ps->reproject_ibuf_free_uchar) { + imb_freerectImBuf(ps->reproject_ibuf); + } + BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); + + MEM_freeN(ps->screenCoords); + MEM_freeN(ps->bucketRect); + MEM_freeN(ps->bucketFaces); + MEM_freeN(ps->bucketFlags); + + if (ps->is_shared_user == false) { + if (ps->mat_array != NULL) { + MEM_freeN(ps->mat_array); + } + + /* must be set for non-shared */ + BLI_assert(ps->poly_to_loop_uv || ps->is_shared_user); + if (ps->poly_to_loop_uv) + MEM_freeN((void *)ps->poly_to_loop_uv); + + if (ps->do_layer_clone) + MEM_freeN((void *)ps->poly_to_loop_uv_clone); + if (ps->thread_tot > 1) { + BLI_spin_end(ps->tile_lock); + MEM_freeN((void *)ps->tile_lock); + } + + image_undo_end_locks(); #ifndef PROJ_DEBUG_NOSEAMBLEED - if (ps->seam_bleed_px > 0.0f) { - MEM_freeN(ps->vertFaces); - MEM_freeN(ps->faceSeamFlags); - MEM_freeN(ps->faceWindingFlags); - MEM_freeN(ps->loopSeamData); - MEM_freeN(ps->vertSeams); - } + if (ps->seam_bleed_px > 0.0f) { + MEM_freeN(ps->vertFaces); + MEM_freeN(ps->faceSeamFlags); + MEM_freeN(ps->faceWindingFlags); + MEM_freeN(ps->loopSeamData); + MEM_freeN(ps->vertSeams); + } #endif - if (ps->do_mask_cavity) { - MEM_freeN(ps->cavities); - } + if (ps->do_mask_cavity) { + MEM_freeN(ps->cavities); + } - if (ps->me_eval_free) { - BKE_id_free(NULL, ps->me_eval); - } - ps->me_eval = NULL; - } + if (ps->me_eval_free) { + BKE_id_free(NULL, ps->me_eval); + } + ps->me_eval = NULL; + } - if (ps->blurkernel) { - paint_delete_blur_kernel(ps->blurkernel); - MEM_freeN(ps->blurkernel); - } + if (ps->blurkernel) { + paint_delete_blur_kernel(ps->blurkernel); + MEM_freeN(ps->blurkernel); + } - if (ps->vertFlags) MEM_freeN(ps->vertFlags); + if (ps->vertFlags) + MEM_freeN(ps->vertFlags); - for (a = 0; a < ps->thread_tot; a++) { - BLI_memarena_free(ps->arena_mt[a]); - } + for (a = 0; a < ps->thread_tot; a++) { + BLI_memarena_free(ps->arena_mt[a]); + } } /* 1 = an undo, -1 is a redo. */ static void partial_redraw_single_init(ImagePaintPartialRedraw *pr) { - pr->x1 = INT_MAX; - pr->y1 = INT_MAX; + pr->x1 = INT_MAX; + pr->y1 = INT_MAX; - pr->x2 = -1; - pr->y2 = -1; + pr->x2 = -1; + pr->y2 = -1; - pr->enabled = 1; + pr->enabled = 1; } static void partial_redraw_array_init(ImagePaintPartialRedraw *pr) { - int tot = PROJ_BOUNDBOX_SQUARED; - while (tot--) { - partial_redraw_single_init(pr); - pr++; - } + int tot = PROJ_BOUNDBOX_SQUARED; + while (tot--) { + partial_redraw_single_init(pr); + pr++; + } } - -static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, ImagePaintPartialRedraw *pr_other, int tot) +static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, + ImagePaintPartialRedraw *pr_other, + int tot) { - bool touch = 0; - while (tot--) { - pr->x1 = min_ii(pr->x1, pr_other->x1); - pr->y1 = min_ii(pr->y1, pr_other->y1); + bool touch = 0; + while (tot--) { + pr->x1 = min_ii(pr->x1, pr_other->x1); + pr->y1 = min_ii(pr->y1, pr_other->y1); - pr->x2 = max_ii(pr->x2, pr_other->x2); - pr->y2 = max_ii(pr->y2, pr_other->y2); + pr->x2 = max_ii(pr->x2, pr_other->x2); + pr->y2 = max_ii(pr->y2, pr_other->y2); - if (pr->x2 != -1) - touch = 1; + if (pr->x2 != -1) + touch = 1; - pr++; pr_other++; - } + pr++; + pr_other++; + } - return touch; + return touch; } /* Loop over all images on this mesh and update any we have touched */ static bool project_image_refresh_tagged(ProjPaintState *ps) { - ImagePaintPartialRedraw *pr; - ProjPaintImage *projIma; - int a, i; - bool redraw = false; - - - for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { - if (projIma->touch) { - /* look over each bound cell */ - for (i = 0; i < PROJ_BOUNDBOX_SQUARED; i++) { - pr = &(projIma->partRedrawRect[i]); - if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ - set_imapaintpartial(pr); - imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); - redraw = 1; - } - - partial_redraw_single_init(pr); - } - - /* clear for reuse */ - projIma->touch = 0; - } - } - - return redraw; + ImagePaintPartialRedraw *pr; + ProjPaintImage *projIma; + int a, i; + bool redraw = false; + + for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { + if (projIma->touch) { + /* look over each bound cell */ + for (i = 0; i < PROJ_BOUNDBOX_SQUARED; i++) { + pr = &(projIma->partRedrawRect[i]); + if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ + set_imapaintpartial(pr); + imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); + redraw = 1; + } + + partial_redraw_single_init(pr); + } + + /* clear for reuse */ + projIma->touch = 0; + } + } + + return redraw; } /* run this per painting onto each mouse location */ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) { - if (ps->source == PROJ_SRC_VIEW) { - float min_brush[2], max_brush[2]; - const float radius = ps->brush_size; + if (ps->source == PROJ_SRC_VIEW) { + float min_brush[2], max_brush[2]; + const float radius = ps->brush_size; - /* so we don't have a bucket bounds that is way too small to paint into */ - // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ + /* so we don't have a bucket bounds that is way too small to paint into */ + // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ - min_brush[0] = mval_f[0] - radius; - min_brush[1] = mval_f[1] - radius; + min_brush[0] = mval_f[0] - radius; + min_brush[1] = mval_f[1] - radius; - max_brush[0] = mval_f[0] + radius; - max_brush[1] = mval_f[1] + radius; + max_brush[0] = mval_f[0] + radius; + max_brush[1] = mval_f[1] + radius; - /* offset to make this a valid bucket index */ - project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax); + /* offset to make this a valid bucket index */ + project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax); - /* mouse outside the model areas? */ - if (ps->bucketMin[0] == ps->bucketMax[0] || ps->bucketMin[1] == ps->bucketMax[1]) { - return 0; - } + /* mouse outside the model areas? */ + if (ps->bucketMin[0] == ps->bucketMax[0] || ps->bucketMin[1] == ps->bucketMax[1]) { + return 0; + } - ps->context_bucket_x = ps->bucketMin[0]; - ps->context_bucket_y = ps->bucketMin[1]; - } - else { /* reproject: PROJ_SRC_* */ - ps->bucketMin[0] = 0; - ps->bucketMin[1] = 0; + ps->context_bucket_x = ps->bucketMin[0]; + ps->context_bucket_y = ps->bucketMin[1]; + } + else { /* reproject: PROJ_SRC_* */ + ps->bucketMin[0] = 0; + ps->bucketMin[1] = 0; - ps->bucketMax[0] = ps->buckets_x; - ps->bucketMax[1] = ps->buckets_y; + ps->bucketMax[0] = ps->buckets_x; + ps->bucketMax[1] = ps->buckets_y; - ps->context_bucket_x = 0; - ps->context_bucket_y = 0; - } - return 1; + ps->context_bucket_x = 0; + ps->context_bucket_y = 0; + } + return 1; } - -static bool project_bucket_iter_next( - ProjPaintState *ps, int *bucket_index, - rctf *bucket_bounds, const float mval[2]) +static bool project_bucket_iter_next(ProjPaintState *ps, + int *bucket_index, + rctf *bucket_bounds, + const float mval[2]) { - const int diameter = 2 * ps->brush_size; + const int diameter = 2 * ps->brush_size; - if (ps->thread_tot > 1) - BLI_thread_lock(LOCK_CUSTOM1); + if (ps->thread_tot > 1) + BLI_thread_lock(LOCK_CUSTOM1); - //printf("%d %d\n", ps->context_bucket_x, ps->context_bucket_y); + //printf("%d %d\n", ps->context_bucket_x, ps->context_bucket_y); - for (; ps->context_bucket_y < ps->bucketMax[1]; ps->context_bucket_y++) { - for (; ps->context_bucket_x < ps->bucketMax[0]; ps->context_bucket_x++) { + for (; ps->context_bucket_y < ps->bucketMax[1]; ps->context_bucket_y++) { + for (; ps->context_bucket_x < ps->bucketMax[0]; ps->context_bucket_x++) { - /* use bucket_bounds for project_bucket_isect_circle and project_bucket_init*/ - project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds); + /* use bucket_bounds for project_bucket_isect_circle and project_bucket_init*/ + project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds); - if ((ps->source != PROJ_SRC_VIEW) || - project_bucket_isect_circle(mval, (float)(diameter * diameter), bucket_bounds)) - { - *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); - ps->context_bucket_x++; + if ((ps->source != PROJ_SRC_VIEW) || + project_bucket_isect_circle(mval, (float)(diameter * diameter), bucket_bounds)) { + *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); + ps->context_bucket_x++; - if (ps->thread_tot > 1) - BLI_thread_unlock(LOCK_CUSTOM1); + if (ps->thread_tot > 1) + BLI_thread_unlock(LOCK_CUSTOM1); - return 1; - } - } - ps->context_bucket_x = ps->bucketMin[0]; - } + return 1; + } + } + ps->context_bucket_x = ps->bucketMin[0]; + } - if (ps->thread_tot > 1) - BLI_thread_unlock(LOCK_CUSTOM1); - return 0; + if (ps->thread_tot > 1) + BLI_thread_unlock(LOCK_CUSTOM1); + return 0; } /* Each thread gets one of these, also used as an argument to pass to project_paint_op */ typedef struct ProjectHandle { - /* args */ - ProjPaintState *ps; - float prevmval[2]; - float mval[2]; + /* args */ + ProjPaintState *ps; + float prevmval[2]; + float mval[2]; - /* Annoying but we need to have image bounds per thread, - * then merge into ps->projectPartialRedraws. */ + /* Annoying but we need to have image bounds per thread, + * then merge into ps->projectPartialRedraws. */ - /* array of partial redraws */ - ProjPaintImage *projImages; + /* array of partial redraws */ + ProjPaintImage *projImages; - /* thread settings */ - int thread_index; + /* thread settings */ + int thread_index; - struct ImagePool *pool; + struct ImagePool *pool; } ProjectHandle; static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - const unsigned char *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch; - - if (clone_pt[3]) { - unsigned char clone_rgba[4]; - - clone_rgba[0] = clone_pt[0]; - clone_rgba[1] = clone_pt[1]; - clone_rgba[2] = clone_pt[2]; - clone_rgba[3] = (unsigned char)(clone_pt[3] * mask); - - if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, clone_rgba, ps->blend); - } - else { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, ps->blend); - } - } + const unsigned char *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch; + + if (clone_pt[3]) { + unsigned char clone_rgba[4]; + + clone_rgba[0] = clone_pt[0]; + clone_rgba[1] = clone_pt[1]; + clone_rgba[2] = clone_pt[2]; + clone_rgba[3] = (unsigned char)(clone_pt[3] * mask); + + if (ps->do_masking) { + IMB_blend_color_byte( + projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, clone_rgba, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, ps->blend); + } + } } static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - const float *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.f; + const float *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.f; - if (clone_pt[3]) { - float clone_rgba[4]; + if (clone_pt[3]) { + float clone_rgba[4]; - mul_v4_v4fl(clone_rgba, clone_pt, mask); + mul_v4_v4fl(clone_rgba, clone_pt, mask); - if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, ps->blend); - } - else { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, ps->blend); - } - } + if (ps->do_masking) { + IMB_blend_color_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, ps->blend); + } + } } /** @@ -4609,1342 +4760,1396 @@ static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, fl * accumulation of color greater than 'projPixel->mask' however in the case of smear its not * really that important to be correct as it is with clone and painting */ -static void do_projectpaint_smear( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *smearArena, LinkNode **smearPixels, const float co[2]) +static void do_projectpaint_smear(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *smearArena, + LinkNode **smearPixels, + const float co[2]) { - unsigned char rgba_ub[4]; + unsigned char rgba_ub[4]; - if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1) == 0) - return; + if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1) == 0) + return; - blend_color_interpolate_byte(((ProjPixelClone *)projPixel)->clonepx.ch, projPixel->pixel.ch_pt, rgba_ub, mask); - BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena); + blend_color_interpolate_byte( + ((ProjPixelClone *)projPixel)->clonepx.ch, projPixel->pixel.ch_pt, rgba_ub, mask); + BLI_linklist_prepend_arena(smearPixels, (void *)projPixel, smearArena); } -static void do_projectpaint_smear_f( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *smearArena, LinkNode **smearPixels_f, const float co[2]) +static void do_projectpaint_smear_f(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *smearArena, + LinkNode **smearPixels_f, + const float co[2]) { - float rgba[4]; + float rgba[4]; - if (project_paint_PickColor(ps, co, rgba, NULL, 1) == 0) - return; + if (project_paint_PickColor(ps, co, rgba, NULL, 1) == 0) + return; - blend_color_interpolate_float(((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, mask); - BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); + blend_color_interpolate_float( + ((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, mask); + BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); } -static void do_projectpaint_soften_f( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *softenArena, LinkNode **softenPixels) +static void do_projectpaint_soften_f(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *softenArena, + LinkNode **softenPixels) { - float accum_tot = 0.0f; - int xk, yk; - BlurKernel *kernel = ps->blurkernel; - float *rgba = projPixel->newColor.f; - - /* rather then painting, accumulate surrounding colors */ - zero_v4(rgba); - - for (yk = 0; yk < kernel->side; yk++) { - for (xk = 0; xk < kernel->side; xk++) { - float rgba_tmp[4]; - float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; - - add_v2_v2(co_ofs, projPixel->projCoSS); - - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - float weight = kernel->wdata[xk + yk * kernel->side]; - mul_v4_fl(rgba_tmp, weight); - add_v4_v4(rgba, rgba_tmp); - accum_tot += weight; - } - } - } - - if (LIKELY(accum_tot != 0)) { - mul_v4_fl(rgba, 1.0f / (float)accum_tot); - - if (ps->mode == BRUSH_STROKE_INVERT) { - /* subtract blurred image from normal image gives high pass filter */ - sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba); - - /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid - * colored speckles appearing in final image, and also to check for threshold */ - rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); - if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { - float alpha = projPixel->pixel.f_pt[3]; - projPixel->pixel.f_pt[3] = rgba[3] = mask; - - /* add to enhance edges */ - blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba); - rgba[3] = alpha; - } - else - return; - } - else { - blend_color_interpolate_float(rgba, projPixel->pixel.f_pt, rgba, mask); - } - - BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); - } + float accum_tot = 0.0f; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; + float *rgba = projPixel->newColor.f; + + /* rather then painting, accumulate surrounding colors */ + zero_v4(rgba); + + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } + } + } + + if (LIKELY(accum_tot != 0)) { + mul_v4_fl(rgba, 1.0f / (float)accum_tot); + + if (ps->mode == BRUSH_STROKE_INVERT) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba); + + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = projPixel->pixel.f_pt[3]; + projPixel->pixel.f_pt[3] = rgba[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba); + rgba[3] = alpha; + } + else + return; + } + else { + blend_color_interpolate_float(rgba, projPixel->pixel.f_pt, rgba, mask); + } + + BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); + } } -static void do_projectpaint_soften( - ProjPaintState *ps, ProjPixel *projPixel, float mask, - MemArena *softenArena, LinkNode **softenPixels) +static void do_projectpaint_soften(ProjPaintState *ps, + ProjPixel *projPixel, + float mask, + MemArena *softenArena, + LinkNode **softenPixels) { - float accum_tot = 0; - int xk, yk; - BlurKernel *kernel = ps->blurkernel; - /* convert to byte after */ - float rgba[4]; - - /* rather then painting, accumulate surrounding colors */ - zero_v4(rgba); - - for (yk = 0; yk < kernel->side; yk++) { - for (xk = 0; xk < kernel->side; xk++) { - float rgba_tmp[4]; - float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; - - add_v2_v2(co_ofs, projPixel->projCoSS); - - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - float weight = kernel->wdata[xk + yk * kernel->side]; - mul_v4_fl(rgba_tmp, weight); - add_v4_v4(rgba, rgba_tmp); - accum_tot += weight; - } - } - } - - if (LIKELY(accum_tot != 0)) { - unsigned char *rgba_ub = projPixel->newColor.ch; - - mul_v4_fl(rgba, 1.0f / (float)accum_tot); - - if (ps->mode == BRUSH_STROKE_INVERT) { - float rgba_pixel[4]; - - straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt); - - /* subtract blurred image from normal image gives high pass filter */ - sub_v3_v3v3(rgba, rgba_pixel, rgba); - /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid - * colored speckles appearing in final image, and also to check for threshold */ - rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); - if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { - float alpha = rgba_pixel[3]; - rgba[3] = rgba_pixel[3] = mask; - - /* add to enhance edges */ - blend_color_add_float(rgba, rgba_pixel, rgba); - - rgba[3] = alpha; - premul_float_to_straight_uchar(rgba_ub, rgba); - } - else - return; - } - else { - premul_float_to_straight_uchar(rgba_ub, rgba); - blend_color_interpolate_byte(rgba_ub, projPixel->pixel.ch_pt, rgba_ub, mask); - } - BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); - } + float accum_tot = 0; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; + /* convert to byte after */ + float rgba[4]; + + /* rather then painting, accumulate surrounding colors */ + zero_v4(rgba); + + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } + } + } + + if (LIKELY(accum_tot != 0)) { + unsigned char *rgba_ub = projPixel->newColor.ch; + + mul_v4_fl(rgba, 1.0f / (float)accum_tot); + + if (ps->mode == BRUSH_STROKE_INVERT) { + float rgba_pixel[4]; + + straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt); + + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, rgba_pixel, rgba); + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = rgba_pixel[3]; + rgba[3] = rgba_pixel[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, rgba_pixel, rgba); + + rgba[3] = alpha; + premul_float_to_straight_uchar(rgba_ub, rgba); + } + else + return; + } + else { + premul_float_to_straight_uchar(rgba_ub, rgba); + blend_color_interpolate_byte(rgba_ub, projPixel->pixel.ch_pt, rgba_ub, mask); + } + BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); + } } -static void do_projectpaint_draw( - ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask, - float dither, float u, float v) +static void do_projectpaint_draw(ProjPaintState *ps, + ProjPixel *projPixel, + const float texrgb[3], + float mask, + float dither, + float u, + float v) { - float rgb[3]; - unsigned char rgba_ub[4]; - - if (ps->is_texbrush) { - mul_v3_v3v3(rgb, texrgb, ps->paint_color_linear); - /* TODO(sergey): Support texture paint color space. */ - if (ps->use_colormanagement) { - linearrgb_to_srgb_v3_v3(rgb, rgb); - } - else { - copy_v3_v3(rgb, rgb); - } - } - else { - copy_v3_v3(rgb, ps->paint_color); - } - - if (dither > 0.0f) { - float_to_byte_dither_v3(rgba_ub, rgb, dither, u, v); - } - else { - unit_float_to_uchar_clamp_v3(rgba_ub, rgb); - } - rgba_ub[3] = f_to_char(mask); - - if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); - } - else { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); - } + float rgb[3]; + unsigned char rgba_ub[4]; + + if (ps->is_texbrush) { + mul_v3_v3v3(rgb, texrgb, ps->paint_color_linear); + /* TODO(sergey): Support texture paint color space. */ + if (ps->use_colormanagement) { + linearrgb_to_srgb_v3_v3(rgb, rgb); + } + else { + copy_v3_v3(rgb, rgb); + } + } + else { + copy_v3_v3(rgb, ps->paint_color); + } + + if (dither > 0.0f) { + float_to_byte_dither_v3(rgba_ub, rgb, dither, u, v); + } + else { + unit_float_to_uchar_clamp_v3(rgba_ub, rgb); + } + rgba_ub[3] = f_to_char(mask); + + if (ps->do_masking) { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); + } } -static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, const float texrgb[3], float mask) +static void do_projectpaint_draw_f(ProjPaintState *ps, + ProjPixel *projPixel, + const float texrgb[3], + float mask) { - float rgba[4]; + float rgba[4]; - copy_v3_v3(rgba, ps->paint_color_linear); + copy_v3_v3(rgba, ps->paint_color_linear); - if (ps->is_texbrush) - mul_v3_v3(rgba, texrgb); + if (ps->is_texbrush) + mul_v3_v3(rgba, texrgb); - mul_v3_fl(rgba, mask); - rgba[3] = mask; + mul_v3_fl(rgba, mask); + rgba[3] = mask; - if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); - } - else { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); - } + if (ps->do_masking) { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); + } } static void do_projectpaint_mask(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - unsigned char rgba_ub[4]; - rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f; - rgba_ub[3] = f_to_char(mask); - - if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); - } - else { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); - } + unsigned char rgba_ub[4]; + rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f; + rgba_ub[3] = f_to_char(mask); + + if (ps->do_masking) { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); + } } static void do_projectpaint_mask_f(ProjPaintState *ps, ProjPixel *projPixel, float mask) { - float rgba[4]; - rgba[0] = rgba[1] = rgba[2] = ps->stencil_value; - rgba[3] = mask; - - if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); - } - else { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); - } + float rgba[4]; + rgba[0] = rgba[1] = rgba[2] = ps->stencil_value; + rgba[3] = mask; + + if (ps->do_masking) { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); + } } -static void image_paint_partial_redraw_expand( - ImagePaintPartialRedraw *cell, - const ProjPixel *projPixel) +static void image_paint_partial_redraw_expand(ImagePaintPartialRedraw *cell, + const ProjPixel *projPixel) { - cell->x1 = min_ii(cell->x1, (int)projPixel->x_px); - cell->y1 = min_ii(cell->y1, (int)projPixel->y_px); + cell->x1 = min_ii(cell->x1, (int)projPixel->x_px); + cell->y1 = min_ii(cell->y1, (int)projPixel->y_px); - cell->x2 = max_ii(cell->x2, (int)projPixel->x_px + 1); - cell->y2 = max_ii(cell->y2, (int)projPixel->y_px + 1); + cell->x2 = max_ii(cell->x2, (int)projPixel->x_px + 1); + cell->y2 = max_ii(cell->y2, (int)projPixel->y_px + 1); } /* run this for single and multithreaded painting */ static void *do_projectpaint_thread(void *ph_v) { - /* First unpack args from the struct */ - ProjPaintState *ps = ((ProjectHandle *)ph_v)->ps; - ProjPaintImage *projImages = ((ProjectHandle *)ph_v)->projImages; - const float *lastpos = ((ProjectHandle *)ph_v)->prevmval; - const float *pos = ((ProjectHandle *)ph_v)->mval; - const int thread_index = ((ProjectHandle *)ph_v)->thread_index; - struct ImagePool *pool = ((ProjectHandle *)ph_v)->pool; - /* Done with args from ProjectHandle */ - - LinkNode *node; - ProjPixel *projPixel; - Brush *brush = ps->brush; - - int last_index = -1; - ProjPaintImage *last_projIma = NULL; - ImagePaintPartialRedraw *last_partial_redraw_cell; - - float dist_sq, dist; - - float falloff; - int bucket_index; - bool is_floatbuf = false; - const short tool = ps->tool; - rctf bucket_bounds; - - /* for smear only */ - float pos_ofs[2] = {0}; - float co[2]; - unsigned short mask_short; - const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); - const float brush_radius = ps->brush_size; - /* avoid a square root with every dist comparison */ - const float brush_radius_sq = brush_radius * brush_radius; - - const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? - 0 : (brush->flag & BRUSH_LOCK_ALPHA) != 0; - - LinkNode *smearPixels = NULL; - LinkNode *smearPixels_f = NULL; - /* mem arena for this brush projection only */ - MemArena *smearArena = NULL; - - LinkNode *softenPixels = NULL; - LinkNode *softenPixels_f = NULL; - /* mem arena for this brush projection only */ - MemArena *softenArena = NULL; - - if (tool == PAINT_TOOL_SMEAR) { - pos_ofs[0] = pos[0] - lastpos[0]; - pos_ofs[1] = pos[1] - lastpos[1]; - - smearArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint smear arena"); - } - else if (tool == PAINT_TOOL_SOFTEN) { - 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]); */ - - while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) { - - /* Check this bucket and its faces are initialized */ - if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) { - rctf clip_rect = bucket_bounds; - clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; - clip_rect.xmax += PROJ_PIXEL_TOLERANCE; - clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; - clip_rect.ymax += PROJ_PIXEL_TOLERANCE; - /* No pixels initialized */ - project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); - } - - if (ps->source != PROJ_SRC_VIEW) { - - /* Re-Projection, simple, no brushes! */ - - for (node = ps->bucketRect[bucket_index]; node; node = node->next) { - projPixel = (ProjPixel *)node->link; - - /* copy of code below */ - if (last_index != projPixel->image_index) { - last_index = projPixel->image_index; - last_projIma = projImages + last_index; - - last_projIma->touch = 1; - is_floatbuf = (last_projIma->ibuf->rect_float != NULL); - } - /* end copy */ - - /* fill tools */ - if (ps->source == PROJ_SRC_VIEW_FILL) { - if (brush->flag & BRUSH_USE_GRADIENT) { - /* these could probably be cached instead of being done per pixel */ - float tangent[2]; - float line_len_sq_inv, line_len; - float f; - float color_f[4]; - float p[2] = {projPixel->projCoSS[0] - lastpos[0], projPixel->projCoSS[1] - lastpos[1]}; - - sub_v2_v2v2(tangent, pos, lastpos); - line_len = len_squared_v2(tangent); - line_len_sq_inv = 1.0f / line_len; - line_len = sqrtf(line_len); - - switch (brush->gradient_fill_mode) { - case BRUSH_GRADIENT_LINEAR: - { - f = dot_v2v2(p, tangent) * line_len_sq_inv; - break; - } - case BRUSH_GRADIENT_RADIAL: - default: - { - f = len_v2(p) / line_len; - break; - } - } - BKE_colorband_evaluate(brush->gradient, f, color_f); - color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; - - if (is_floatbuf) { - /* convert to premultipied */ - mul_v3_fl(color_f, color_f[3]); - IMB_blend_color_float( - projPixel->pixel.f_pt, projPixel->origColor.f_pt, - color_f, ps->blend); - } - else { - linearrgb_to_srgb_v3_v3(color_f, color_f); - - if (ps->dither > 0.0f) { - float_to_byte_dither_v3(projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); - } - else { - unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f); - } - projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]); - IMB_blend_color_byte( - projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, - projPixel->newColor.ch, ps->blend); - } - } - else { - if (is_floatbuf) { - float newColor_f[4]; - newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; - copy_v3_v3(newColor_f, ps->paint_color_linear); - - IMB_blend_color_float( - projPixel->pixel.f_pt, projPixel->origColor.f_pt, - newColor_f, ps->blend); - } - else { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - projPixel->newColor.ch[3] = mask * 255 * brush->alpha; - - rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); - IMB_blend_color_byte( - projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, - projPixel->newColor.ch, ps->blend); - } - } - - if (lock_alpha) { - if (is_floatbuf) { - /* slightly more involved case since floats are in premultiplied space we need - * to make sure alpha is consistent, see T44627 */ - float rgb_straight[4]; - premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); - rgb_straight[3] = projPixel->origColor.f_pt[3]; - straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); - } - else { - projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; - } - } - - last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; - image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); - } - else { - if (is_floatbuf) { - if (UNLIKELY(ps->reproject_ibuf->rect_float == NULL)) { - IMB_float_from_rect(ps->reproject_ibuf); - ps->reproject_ibuf_free_float = true; - } - - bicubic_interpolation_color( - ps->reproject_ibuf, NULL, projPixel->newColor.f, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.f[3]) { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - - mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask); - - blend_color_mix_float( - projPixel->pixel.f_pt, projPixel->origColor.f_pt, - projPixel->newColor.f); - } - } - else { - if (UNLIKELY(ps->reproject_ibuf->rect == NULL)) { - IMB_rect_from_float(ps->reproject_ibuf); - ps->reproject_ibuf_free_uchar = true; - } - - bicubic_interpolation_color( - ps->reproject_ibuf, projPixel->newColor.ch, NULL, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.ch[3]) { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - projPixel->newColor.ch[3] *= mask; - - blend_color_mix_byte( - projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, - projPixel->newColor.ch); - } - } - } - } - } - else { - /* Normal brush painting */ - - for (node = ps->bucketRect[bucket_index]; node; node = node->next) { - - projPixel = (ProjPixel *)node->link; - - dist_sq = len_squared_v2v2(projPixel->projCoSS, pos); - - /*if (dist < radius) {*/ /* correct but uses a sqrtf */ - if (dist_sq <= brush_radius_sq) { - dist = sqrtf(dist_sq); - - falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); - - if (falloff > 0.0f) { - float texrgb[3]; - float mask; - - /* Extra mask for normal, layer stencil, .. */ - float custom_mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - - /* Mask texture. */ - if (ps->is_maskbrush) { - float texmask = BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); - CLAMP(texmask, 0.0f, 1.0f); - custom_mask *= texmask; - } - - /* Color texture (alpha used as mask). */ - if (ps->is_texbrush) { - MTex *mtex = &brush->mtex; - float samplecos[3]; - float texrgba[4]; - - /* taking 3d copy to account for 3D mapping too. - * It gets concatenated during sampling */ - if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { - copy_v3_v3(samplecos, projPixel->worldCoSS); - } - else { - copy_v2_v2(samplecos, projPixel->projCoSS); - samplecos[2] = 0.0f; - } - - /* note, for clone and smear, - * we only use the alpha, could be a special function */ - BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); - - copy_v3_v3(texrgb, texrgba); - custom_mask *= texrgba[3]; - } - else { - zero_v3(texrgb); - } - - if (ps->do_masking) { - /* masking to keep brush contribution to a pixel limited. note we do not do - * a simple max(mask, mask_accum), as this is very sensitive to spacing and - * gives poor results for strokes crossing themselves. - * - * Instead we use a formula that adds up but approaches brush_alpha slowly - * and never exceeds it, which gives nice smooth results. */ - float mask_accum = *projPixel->mask_accum; - float max_mask = brush_alpha * custom_mask * falloff * 65535.0f; - - if (brush->flag & BRUSH_ACCUMULATE) - mask = mask_accum + max_mask; - else - mask = mask_accum + (max_mask - mask_accum * falloff); - - mask = min_ff(mask, 65535.0f); - mask_short = (unsigned short)mask; - - if (mask_short > *projPixel->mask_accum) { - *projPixel->mask_accum = mask_short; - mask = mask_short * (1.0f / 65535.0f); - } - else { - /* Go onto the next pixel */ - continue; - } - } - else { - mask = brush_alpha * custom_mask * falloff; - } - - if (mask > 0.0f) { - - /* copy of code above */ - if (last_index != projPixel->image_index) { - last_index = projPixel->image_index; - last_projIma = projImages + last_index; - - last_projIma->touch = 1; - is_floatbuf = (last_projIma->ibuf->rect_float != NULL); - } - /* end copy */ - - /* validate undo tile, since we will modify t*/ - *projPixel->valid = true; - - last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; - image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); - - /* texrgb is not used for clone, smear or soften */ - switch (tool) { - case PAINT_TOOL_CLONE: - if (is_floatbuf) do_projectpaint_clone_f(ps, projPixel, mask); - else do_projectpaint_clone(ps, projPixel, mask); - break; - case PAINT_TOOL_SMEAR: - sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs); - - if (is_floatbuf) do_projectpaint_smear_f(ps, projPixel, mask, smearArena, &smearPixels_f, co); - else do_projectpaint_smear(ps, projPixel, mask, smearArena, &smearPixels, co); - break; - case PAINT_TOOL_SOFTEN: - if (is_floatbuf) do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); - else do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); - break; - case PAINT_TOOL_MASK: - if (is_floatbuf) do_projectpaint_mask_f(ps, projPixel, mask); - else do_projectpaint_mask(ps, projPixel, mask); - break; - default: - if (is_floatbuf) do_projectpaint_draw_f(ps, projPixel, texrgb, mask); - else do_projectpaint_draw(ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); - break; - } - - if (lock_alpha) { - if (is_floatbuf) { - /* slightly more involved case since floats are in premultiplied space we need - * to make sure alpha is consistent, see T44627 */ - float rgb_straight[4]; - premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); - rgb_straight[3] = projPixel->origColor.f_pt[3]; - straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); - } - else { - projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; - } - } - } - - /* done painting */ - } - } - } - } - } - - - if (tool == PAINT_TOOL_SMEAR) { - - for (node = smearPixels; node; node = node->next) { /* this wont run for a float image */ - projPixel = node->link; - *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint; - } - - for (node = smearPixels_f; node; node = node->next) { - projPixel = node->link; - copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); - } - - BLI_memarena_free(smearArena); - } - else if (tool == PAINT_TOOL_SOFTEN) { - - for (node = softenPixels; node; node = node->next) { /* this wont run for a float image */ - projPixel = node->link; - *projPixel->pixel.uint_pt = projPixel->newColor.uint; - } - - for (node = softenPixels_f; node; node = node->next) { - projPixel = node->link; - copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f); - } - - BLI_memarena_free(softenArena); - } - - return NULL; + /* First unpack args from the struct */ + ProjPaintState *ps = ((ProjectHandle *)ph_v)->ps; + ProjPaintImage *projImages = ((ProjectHandle *)ph_v)->projImages; + const float *lastpos = ((ProjectHandle *)ph_v)->prevmval; + const float *pos = ((ProjectHandle *)ph_v)->mval; + const int thread_index = ((ProjectHandle *)ph_v)->thread_index; + struct ImagePool *pool = ((ProjectHandle *)ph_v)->pool; + /* Done with args from ProjectHandle */ + + LinkNode *node; + ProjPixel *projPixel; + Brush *brush = ps->brush; + + int last_index = -1; + ProjPaintImage *last_projIma = NULL; + ImagePaintPartialRedraw *last_partial_redraw_cell; + + float dist_sq, dist; + + float falloff; + int bucket_index; + bool is_floatbuf = false; + const short tool = ps->tool; + rctf bucket_bounds; + + /* for smear only */ + float pos_ofs[2] = {0}; + float co[2]; + unsigned short mask_short; + const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); + const float brush_radius = ps->brush_size; + /* avoid a square root with every dist comparison */ + const float brush_radius_sq = brush_radius * brush_radius; + + const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? + 0 : + (brush->flag & BRUSH_LOCK_ALPHA) != 0; + + LinkNode *smearPixels = NULL; + LinkNode *smearPixels_f = NULL; + /* mem arena for this brush projection only */ + MemArena *smearArena = NULL; + + LinkNode *softenPixels = NULL; + LinkNode *softenPixels_f = NULL; + /* mem arena for this brush projection only */ + MemArena *softenArena = NULL; + + if (tool == PAINT_TOOL_SMEAR) { + pos_ofs[0] = pos[0] - lastpos[0]; + pos_ofs[1] = pos[1] - lastpos[1]; + + smearArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint smear arena"); + } + else if (tool == PAINT_TOOL_SOFTEN) { + 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]); */ + + while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) { + + /* Check this bucket and its faces are initialized */ + if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) { + rctf clip_rect = bucket_bounds; + clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; + clip_rect.xmax += PROJ_PIXEL_TOLERANCE; + clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; + clip_rect.ymax += PROJ_PIXEL_TOLERANCE; + /* No pixels initialized */ + project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); + } + + if (ps->source != PROJ_SRC_VIEW) { + + /* Re-Projection, simple, no brushes! */ + + for (node = ps->bucketRect[bucket_index]; node; node = node->next) { + projPixel = (ProjPixel *)node->link; + + /* copy of code below */ + if (last_index != projPixel->image_index) { + last_index = projPixel->image_index; + last_projIma = projImages + last_index; + + last_projIma->touch = 1; + is_floatbuf = (last_projIma->ibuf->rect_float != NULL); + } + /* end copy */ + + /* fill tools */ + if (ps->source == PROJ_SRC_VIEW_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + /* these could probably be cached instead of being done per pixel */ + float tangent[2]; + float line_len_sq_inv, line_len; + float f; + float color_f[4]; + float p[2] = {projPixel->projCoSS[0] - lastpos[0], + projPixel->projCoSS[1] - lastpos[1]}; + + sub_v2_v2v2(tangent, pos, lastpos); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrtf(line_len); + + switch (brush->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + default: { + f = len_v2(p) / line_len; + break; + } + } + BKE_colorband_evaluate(brush->gradient, f, color_f); + color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + + if (is_floatbuf) { + /* convert to premultipied */ + mul_v3_fl(color_f, color_f[3]); + IMB_blend_color_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, color_f, ps->blend); + } + else { + linearrgb_to_srgb_v3_v3(color_f, color_f); + + if (ps->dither > 0.0f) { + float_to_byte_dither_v3( + projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); + } + else { + unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f); + } + projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]); + IMB_blend_color_byte(projPixel->pixel.ch_pt, + projPixel->origColor.ch_pt, + projPixel->newColor.ch, + ps->blend); + } + } + else { + if (is_floatbuf) { + float newColor_f[4]; + newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + copy_v3_v3(newColor_f, ps->paint_color_linear); + + IMB_blend_color_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, newColor_f, ps->blend); + } + else { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] = mask * 255 * brush->alpha; + + rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); + IMB_blend_color_byte(projPixel->pixel.ch_pt, + projPixel->origColor.ch_pt, + projPixel->newColor.ch, + ps->blend); + } + } + + if (lock_alpha) { + if (is_floatbuf) { + /* slightly more involved case since floats are in premultiplied space we need + * to make sure alpha is consistent, see T44627 */ + float rgb_straight[4]; + premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); + rgb_straight[3] = projPixel->origColor.f_pt[3]; + straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); + } + else { + projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + } + } + + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; + image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); + } + else { + if (is_floatbuf) { + if (UNLIKELY(ps->reproject_ibuf->rect_float == NULL)) { + IMB_float_from_rect(ps->reproject_ibuf); + ps->reproject_ibuf_free_float = true; + } + + bicubic_interpolation_color(ps->reproject_ibuf, + NULL, + projPixel->newColor.f, + projPixel->projCoSS[0], + projPixel->projCoSS[1]); + if (projPixel->newColor.f[3]) { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + + mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask); + + blend_color_mix_float( + projPixel->pixel.f_pt, projPixel->origColor.f_pt, projPixel->newColor.f); + } + } + else { + if (UNLIKELY(ps->reproject_ibuf->rect == NULL)) { + IMB_rect_from_float(ps->reproject_ibuf); + ps->reproject_ibuf_free_uchar = true; + } + + bicubic_interpolation_color(ps->reproject_ibuf, + projPixel->newColor.ch, + NULL, + projPixel->projCoSS[0], + projPixel->projCoSS[1]); + if (projPixel->newColor.ch[3]) { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] *= mask; + + blend_color_mix_byte( + projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, projPixel->newColor.ch); + } + } + } + } + } + else { + /* Normal brush painting */ + + for (node = ps->bucketRect[bucket_index]; node; node = node->next) { + + projPixel = (ProjPixel *)node->link; + + dist_sq = len_squared_v2v2(projPixel->projCoSS, pos); + + /*if (dist < radius) {*/ /* correct but uses a sqrtf */ + if (dist_sq <= brush_radius_sq) { + dist = sqrtf(dist_sq); + + falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); + + if (falloff > 0.0f) { + float texrgb[3]; + float mask; + + /* Extra mask for normal, layer stencil, .. */ + float custom_mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + + /* Mask texture. */ + if (ps->is_maskbrush) { + float texmask = BKE_brush_sample_masktex( + ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); + CLAMP(texmask, 0.0f, 1.0f); + custom_mask *= texmask; + } + + /* Color texture (alpha used as mask). */ + if (ps->is_texbrush) { + MTex *mtex = &brush->mtex; + float samplecos[3]; + float texrgba[4]; + + /* taking 3d copy to account for 3D mapping too. + * It gets concatenated during sampling */ + if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + copy_v3_v3(samplecos, projPixel->worldCoSS); + } + else { + copy_v2_v2(samplecos, projPixel->projCoSS); + samplecos[2] = 0.0f; + } + + /* note, for clone and smear, + * we only use the alpha, could be a special function */ + BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); + + copy_v3_v3(texrgb, texrgba); + custom_mask *= texrgba[3]; + } + else { + zero_v3(texrgb); + } + + if (ps->do_masking) { + /* masking to keep brush contribution to a pixel limited. note we do not do + * a simple max(mask, mask_accum), as this is very sensitive to spacing and + * gives poor results for strokes crossing themselves. + * + * Instead we use a formula that adds up but approaches brush_alpha slowly + * and never exceeds it, which gives nice smooth results. */ + float mask_accum = *projPixel->mask_accum; + float max_mask = brush_alpha * custom_mask * falloff * 65535.0f; + + if (brush->flag & BRUSH_ACCUMULATE) + mask = mask_accum + max_mask; + else + mask = mask_accum + (max_mask - mask_accum * falloff); + + mask = min_ff(mask, 65535.0f); + mask_short = (unsigned short)mask; + + if (mask_short > *projPixel->mask_accum) { + *projPixel->mask_accum = mask_short; + mask = mask_short * (1.0f / 65535.0f); + } + else { + /* Go onto the next pixel */ + continue; + } + } + else { + mask = brush_alpha * custom_mask * falloff; + } + + if (mask > 0.0f) { + + /* copy of code above */ + if (last_index != projPixel->image_index) { + last_index = projPixel->image_index; + last_projIma = projImages + last_index; + + last_projIma->touch = 1; + is_floatbuf = (last_projIma->ibuf->rect_float != NULL); + } + /* end copy */ + + /* validate undo tile, since we will modify t*/ + *projPixel->valid = true; + + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; + image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); + + /* texrgb is not used for clone, smear or soften */ + switch (tool) { + case PAINT_TOOL_CLONE: + if (is_floatbuf) + do_projectpaint_clone_f(ps, projPixel, mask); + else + do_projectpaint_clone(ps, projPixel, mask); + break; + case PAINT_TOOL_SMEAR: + sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs); + + if (is_floatbuf) + do_projectpaint_smear_f(ps, projPixel, mask, smearArena, &smearPixels_f, co); + else + do_projectpaint_smear(ps, projPixel, mask, smearArena, &smearPixels, co); + break; + case PAINT_TOOL_SOFTEN: + if (is_floatbuf) + do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); + else + do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); + break; + case PAINT_TOOL_MASK: + if (is_floatbuf) + do_projectpaint_mask_f(ps, projPixel, mask); + else + do_projectpaint_mask(ps, projPixel, mask); + break; + default: + if (is_floatbuf) + do_projectpaint_draw_f(ps, projPixel, texrgb, mask); + else + do_projectpaint_draw( + ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); + break; + } + + if (lock_alpha) { + if (is_floatbuf) { + /* slightly more involved case since floats are in premultiplied space we need + * to make sure alpha is consistent, see T44627 */ + float rgb_straight[4]; + premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); + rgb_straight[3] = projPixel->origColor.f_pt[3]; + straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); + } + else { + projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + } + } + } + + /* done painting */ + } + } + } + } + } + + if (tool == PAINT_TOOL_SMEAR) { + + for (node = smearPixels; node; node = node->next) { /* this wont run for a float image */ + projPixel = node->link; + *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint; + } + + for (node = smearPixels_f; node; node = node->next) { + projPixel = node->link; + copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); + } + + BLI_memarena_free(smearArena); + } + else if (tool == PAINT_TOOL_SOFTEN) { + + for (node = softenPixels; node; node = node->next) { /* this wont run for a float image */ + projPixel = node->link; + *projPixel->pixel.uint_pt = projPixel->newColor.uint; + } + + for (node = softenPixels_f; node; node = node->next) { + projPixel = node->link; + copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f); + } + + BLI_memarena_free(softenArena); + } + + return NULL; } static bool project_paint_op(void *state, const float lastpos[2], const float pos[2]) { - /* First unpack args from the struct */ - ProjPaintState *ps = (ProjPaintState *)state; - bool touch_any = false; - - ProjectHandle handles[BLENDER_MAX_THREADS]; - ListBase threads; - int a, i; - - struct ImagePool *pool; - - if (!project_bucket_iter_init(ps, pos)) { - return touch_any; - } - - if (ps->thread_tot > 1) - BLI_threadpool_init(&threads, do_projectpaint_thread, ps->thread_tot); - - pool = BKE_image_pool_new(); - - /* get the threads running */ - for (a = 0; a < ps->thread_tot; a++) { - - /* set defaults in handles */ - //memset(&handles[a], 0, sizeof(BakeShade)); - - handles[a].ps = ps; - copy_v2_v2(handles[a].mval, pos); - copy_v2_v2(handles[a].prevmval, lastpos); - - /* thread specific */ - handles[a].thread_index = a; - - handles[a].projImages = BLI_memarena_alloc(ps->arena_mt[a], ps->image_tot * sizeof(ProjPaintImage)); - - memcpy(handles[a].projImages, ps->projImages, ps->image_tot * sizeof(ProjPaintImage)); - - /* image bounds */ - for (i = 0; i < ps->image_tot; i++) { - handles[a].projImages[i].partRedrawRect = BLI_memarena_alloc(ps->arena_mt[a], sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); - memcpy(handles[a].projImages[i].partRedrawRect, ps->projImages[i].partRedrawRect, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); - } - - handles[a].pool = pool; - - if (ps->thread_tot > 1) - BLI_threadpool_insert(&threads, &handles[a]); - } - - if (ps->thread_tot > 1) /* wait for everything to be done */ - BLI_threadpool_end(&threads); - else - do_projectpaint_thread(&handles[0]); - - - BKE_image_pool_free(pool); - - /* move threaded bounds back into ps->projectPartialRedraws */ - for (i = 0; i < ps->image_tot; i++) { - int touch = 0; - for (a = 0; a < ps->thread_tot; a++) { - touch |= partial_redraw_array_merge(ps->projImages[i].partRedrawRect, handles[a].projImages[i].partRedrawRect, PROJ_BOUNDBOX_SQUARED); - } - - if (touch) { - ps->projImages[i].touch = 1; - touch_any = 1; - } - } - - /* calculate pivot for rotation around seletion if needed */ - if (U.uiflag & USER_ORBIT_SELECTION) { - float w[3]; - int tri_index; - - tri_index = project_paint_PickFace(ps, pos, w); - - if (tri_index != -1) { - const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; - const int lt_vtri[3] = { PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt) }; - float world[3]; - UnifiedPaintSettings *ups = &ps->scene->toolsettings->unified_paint_settings; - - interp_v3_v3v3v3( - world, - ps->mvert_eval[lt_vtri[0]].co, - ps->mvert_eval[lt_vtri[1]].co, - ps->mvert_eval[lt_vtri[2]].co, - w); - - ups->average_stroke_counter++; - mul_m4_v3(ps->obmat, world); - add_v3_v3(ups->average_stroke_accum, world); - ups->last_stroke_valid = true; - } - } - - return touch_any; + /* First unpack args from the struct */ + ProjPaintState *ps = (ProjPaintState *)state; + bool touch_any = false; + + ProjectHandle handles[BLENDER_MAX_THREADS]; + ListBase threads; + int a, i; + + struct ImagePool *pool; + + if (!project_bucket_iter_init(ps, pos)) { + return touch_any; + } + + if (ps->thread_tot > 1) + BLI_threadpool_init(&threads, do_projectpaint_thread, ps->thread_tot); + + pool = BKE_image_pool_new(); + + /* get the threads running */ + for (a = 0; a < ps->thread_tot; a++) { + + /* set defaults in handles */ + //memset(&handles[a], 0, sizeof(BakeShade)); + + handles[a].ps = ps; + copy_v2_v2(handles[a].mval, pos); + copy_v2_v2(handles[a].prevmval, lastpos); + + /* thread specific */ + handles[a].thread_index = a; + + handles[a].projImages = BLI_memarena_alloc(ps->arena_mt[a], + ps->image_tot * sizeof(ProjPaintImage)); + + memcpy(handles[a].projImages, ps->projImages, ps->image_tot * sizeof(ProjPaintImage)); + + /* image bounds */ + for (i = 0; i < ps->image_tot; i++) { + handles[a].projImages[i].partRedrawRect = BLI_memarena_alloc( + ps->arena_mt[a], sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + memcpy(handles[a].projImages[i].partRedrawRect, + ps->projImages[i].partRedrawRect, + sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + } + + handles[a].pool = pool; + + if (ps->thread_tot > 1) + BLI_threadpool_insert(&threads, &handles[a]); + } + + if (ps->thread_tot > 1) /* wait for everything to be done */ + BLI_threadpool_end(&threads); + else + do_projectpaint_thread(&handles[0]); + + BKE_image_pool_free(pool); + + /* move threaded bounds back into ps->projectPartialRedraws */ + for (i = 0; i < ps->image_tot; i++) { + int touch = 0; + for (a = 0; a < ps->thread_tot; a++) { + touch |= partial_redraw_array_merge(ps->projImages[i].partRedrawRect, + handles[a].projImages[i].partRedrawRect, + PROJ_BOUNDBOX_SQUARED); + } + + if (touch) { + ps->projImages[i].touch = 1; + touch_any = 1; + } + } + + /* calculate pivot for rotation around seletion if needed */ + if (U.uiflag & USER_ORBIT_SELECTION) { + float w[3]; + int tri_index; + + tri_index = project_paint_PickFace(ps, pos, w); + + if (tri_index != -1) { + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; + float world[3]; + UnifiedPaintSettings *ups = &ps->scene->toolsettings->unified_paint_settings; + + interp_v3_v3v3v3(world, + ps->mvert_eval[lt_vtri[0]].co, + ps->mvert_eval[lt_vtri[1]].co, + ps->mvert_eval[lt_vtri[2]].co, + w); + + ups->average_stroke_counter++; + mul_m4_v3(ps->obmat, world); + add_v3_v3(ups->average_stroke_accum, world); + ups->last_stroke_valid = true; + } + } + + return touch_any; } - -static void paint_proj_stroke_ps( - const bContext *UNUSED(C), void *ps_handle_p, const float prev_pos[2], const float pos[2], - const bool eraser, float pressure, float distance, float size, - /* extra view */ - ProjPaintState *ps - ) +static void paint_proj_stroke_ps(const bContext *UNUSED(C), + void *ps_handle_p, + const float prev_pos[2], + const float pos[2], + const bool eraser, + float pressure, + float distance, + float size, + /* extra view */ + ProjPaintState *ps) { - ProjStrokeHandle *ps_handle = ps_handle_p; - Brush *brush = ps->brush; - Scene *scene = ps->scene; - - ps->brush_size = size; - ps->blend = brush->blend; - if (eraser) - ps->blend = IMB_BLEND_ERASE_ALPHA; - - /* handle gradient and inverted stroke color here */ - if (ELEM(ps->tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) { - paint_brush_color_get(scene, brush, false, ps->mode == BRUSH_STROKE_INVERT, distance, pressure, ps->paint_color, NULL); - if (ps->use_colormanagement) { - srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); - } - else { - copy_v3_v3(ps->paint_color_linear, ps->paint_color); - } - } - else if (ps->tool == PAINT_TOOL_MASK) { - ps->stencil_value = brush->weight; - - if ((ps->mode == BRUSH_STROKE_INVERT) ^ - ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0)) - { - ps->stencil_value = 1.0f - ps->stencil_value; - } - } - - if (project_paint_op(ps, prev_pos, pos)) { - ps_handle->need_redraw = true; - project_image_refresh_tagged(ps); - } + ProjStrokeHandle *ps_handle = ps_handle_p; + Brush *brush = ps->brush; + Scene *scene = ps->scene; + + ps->brush_size = size; + ps->blend = brush->blend; + if (eraser) + ps->blend = IMB_BLEND_ERASE_ALPHA; + + /* handle gradient and inverted stroke color here */ + if (ELEM(ps->tool, PAINT_TOOL_DRAW, PAINT_TOOL_FILL)) { + paint_brush_color_get(scene, + brush, + false, + ps->mode == BRUSH_STROKE_INVERT, + distance, + pressure, + ps->paint_color, + NULL); + if (ps->use_colormanagement) { + srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); + } + else { + copy_v3_v3(ps->paint_color_linear, ps->paint_color); + } + } + else if (ps->tool == PAINT_TOOL_MASK) { + ps->stencil_value = brush->weight; + + if ((ps->mode == BRUSH_STROKE_INVERT) ^ + ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0)) { + ps->stencil_value = 1.0f - ps->stencil_value; + } + } + + if (project_paint_op(ps, prev_pos, pos)) { + ps_handle->need_redraw = true; + project_image_refresh_tagged(ps); + } } - -void paint_proj_stroke( - const bContext *C, void *ps_handle_p, const float prev_pos[2], const float pos[2], - const bool eraser, float pressure, float distance, float size) +void paint_proj_stroke(const bContext *C, + void *ps_handle_p, + const float prev_pos[2], + const float pos[2], + const bool eraser, + float pressure, + float distance, + float size) { - int i; - ProjStrokeHandle *ps_handle = ps_handle_p; + int i; + ProjStrokeHandle *ps_handle = ps_handle_p; - /* clone gets special treatment here to avoid going through image initialization */ - if (ps_handle->is_clone_cursor_pick) { - Scene *scene = ps_handle->scene; - struct Depsgraph *depsgraph = CTX_data_depsgraph(C); - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); - float *cursor = scene->cursor.location; - int mval_i[2] = {(int)pos[0], (int)pos[1]}; + /* clone gets special treatment here to avoid going through image initialization */ + if (ps_handle->is_clone_cursor_pick) { + Scene *scene = ps_handle->scene; + struct Depsgraph *depsgraph = CTX_data_depsgraph(C); + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + float *cursor = scene->cursor.location; + int mval_i[2] = {(int)pos[0], (int)pos[1]}; - view3d_operator_needs_opengl(C); + view3d_operator_needs_opengl(C); - if (!ED_view3d_autodist(depsgraph, ar, v3d, mval_i, cursor, false, NULL)) { - return; - } + if (!ED_view3d_autodist(depsgraph, ar, v3d, mval_i, cursor, false, NULL)) { + return; + } - ED_region_tag_redraw(ar); + ED_region_tag_redraw(ar); - return; - } + return; + } - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps); - } + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps); + } } - /* initialize project paint settings from context */ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int mode) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - - /* brush */ - ps->mode = mode; - ps->brush = BKE_paint_brush(&settings->imapaint.paint); - if (ps->brush) { - Brush *brush = ps->brush; - ps->tool = brush->imagepaint_tool; - ps->blend = brush->blend; - /* only check for inversion for the soften tool, elsewhere, - * a resident brush inversion flag can cause issues */ - if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { - ps->mode = (((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0)) ? - BRUSH_STROKE_INVERT : BRUSH_STROKE_NORMAL); - - ps->blurkernel = paint_new_blur_kernel(brush, true); - } - - /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ - ps->do_masking = paint_use_opacity_masking(brush); - ps->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false; - } - else { - /* brush may be NULL*/ - ps->do_masking = false; - ps->is_texbrush = false; - ps->is_maskbrush = false; - } - - /* sizeof(ProjPixel), since we alloc this a _lot_ */ - ps->pixel_sizeof = project_paint_pixel_sizeof(ps->tool); - BLI_assert(ps->pixel_sizeof >= sizeof(ProjPixel)); - - /* these can be NULL */ - ps->v3d = CTX_wm_view3d(C); - ps->rv3d = CTX_wm_region_view3d(C); - ps->ar = CTX_wm_region(C); - - ps->depsgraph = CTX_data_depsgraph(C); - ps->scene = scene; - /* allow override of active object */ - ps->ob = ob; - - ps->do_material_slots = (settings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL); - ps->stencil_ima = settings->imapaint.stencil; - ps->canvas_ima = (!ps->do_material_slots) ? - settings->imapaint.canvas : NULL; - ps->clone_ima = (!ps->do_material_slots) ? - settings->imapaint.clone : NULL; - - ps->do_mask_cavity = (settings->imapaint.paint.flags & PAINT_USE_CAVITY_MASK) ? true : false; - ps->cavity_curve = settings->imapaint.paint.cavity_curve; - - /* setup projection painting data */ - if (ps->tool != PAINT_TOOL_FILL) { - ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? false : true; - ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? false : true; - ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? false : true; - } - else { - ps->do_backfacecull = ps->do_occlude = ps->do_mask_normal = 0; - } - - if (ps->tool == PAINT_TOOL_CLONE) - ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) ? 1 : 0; - - - ps->do_stencil_brush = (ps->brush && ps->brush->imagepaint_tool == PAINT_TOOL_MASK); - /* deactivate stenciling for the stencil brush :) */ - ps->do_layer_stencil = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) && - !(ps->do_stencil_brush) && ps->stencil_ima); - ps->do_layer_stencil_inv = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0); - + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + + /* brush */ + ps->mode = mode; + ps->brush = BKE_paint_brush(&settings->imapaint.paint); + if (ps->brush) { + Brush *brush = ps->brush; + ps->tool = brush->imagepaint_tool; + ps->blend = brush->blend; + /* only check for inversion for the soften tool, elsewhere, + * a resident brush inversion flag can cause issues */ + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + ps->mode = (((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0)) ? + BRUSH_STROKE_INVERT : + BRUSH_STROKE_NORMAL); + + ps->blurkernel = paint_new_blur_kernel(brush, true); + } + + /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ + ps->do_masking = paint_use_opacity_masking(brush); + ps->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : + false; + ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false; + } + else { + /* brush may be NULL*/ + ps->do_masking = false; + ps->is_texbrush = false; + ps->is_maskbrush = false; + } + + /* sizeof(ProjPixel), since we alloc this a _lot_ */ + ps->pixel_sizeof = project_paint_pixel_sizeof(ps->tool); + BLI_assert(ps->pixel_sizeof >= sizeof(ProjPixel)); + + /* these can be NULL */ + ps->v3d = CTX_wm_view3d(C); + ps->rv3d = CTX_wm_region_view3d(C); + ps->ar = CTX_wm_region(C); + + ps->depsgraph = CTX_data_depsgraph(C); + ps->scene = scene; + /* allow override of active object */ + ps->ob = ob; + + ps->do_material_slots = (settings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL); + ps->stencil_ima = settings->imapaint.stencil; + ps->canvas_ima = (!ps->do_material_slots) ? settings->imapaint.canvas : NULL; + ps->clone_ima = (!ps->do_material_slots) ? settings->imapaint.clone : NULL; + + ps->do_mask_cavity = (settings->imapaint.paint.flags & PAINT_USE_CAVITY_MASK) ? true : false; + ps->cavity_curve = settings->imapaint.paint.cavity_curve; + + /* setup projection painting data */ + if (ps->tool != PAINT_TOOL_FILL) { + ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? false : true; + ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? false : true; + ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? false : true; + } + else { + ps->do_backfacecull = ps->do_occlude = ps->do_mask_normal = 0; + } + + if (ps->tool == PAINT_TOOL_CLONE) + ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) ? 1 : 0; + + ps->do_stencil_brush = (ps->brush && ps->brush->imagepaint_tool == PAINT_TOOL_MASK); + /* deactivate stenciling for the stencil brush :) */ + ps->do_layer_stencil = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) && + !(ps->do_stencil_brush) && ps->stencil_ima); + ps->do_layer_stencil_inv = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != + 0); #ifndef PROJ_DEBUG_NOSEAMBLEED - /* pixel num to bleed */ - ps->seam_bleed_px = settings->imapaint.seam_bleed; - ps->seam_bleed_px_sq = SQUARE(settings->imapaint.seam_bleed); + /* pixel num to bleed */ + ps->seam_bleed_px = settings->imapaint.seam_bleed; + ps->seam_bleed_px_sq = SQUARE(settings->imapaint.seam_bleed); #endif - if (ps->do_mask_normal) { - ps->normal_angle_inner = settings->imapaint.normal_angle; - ps->normal_angle = (ps->normal_angle_inner + 90.0f) * 0.5f; - } - else { - ps->normal_angle_inner = ps->normal_angle = settings->imapaint.normal_angle; - } + if (ps->do_mask_normal) { + ps->normal_angle_inner = settings->imapaint.normal_angle; + ps->normal_angle = (ps->normal_angle_inner + 90.0f) * 0.5f; + } + else { + ps->normal_angle_inner = ps->normal_angle = settings->imapaint.normal_angle; + } - ps->normal_angle_inner *= (float)(M_PI_2 / 90); - ps->normal_angle *= (float)(M_PI_2 / 90); - ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner; + ps->normal_angle_inner *= (float)(M_PI_2 / 90); + ps->normal_angle *= (float)(M_PI_2 / 90); + ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner; - if (ps->normal_angle_range <= 0.0f) { - /* no need to do blending */ - ps->do_mask_normal = false; - } + if (ps->normal_angle_range <= 0.0f) { + /* no need to do blending */ + ps->do_mask_normal = false; + } - ps->normal_angle__cos = cosf(ps->normal_angle); - ps->normal_angle_inner__cos = cosf(ps->normal_angle_inner); + ps->normal_angle__cos = cosf(ps->normal_angle); + ps->normal_angle_inner__cos = cosf(ps->normal_angle_inner); - ps->dither = settings->imapaint.dither; + ps->dither = settings->imapaint.dither; - ps->use_colormanagement = BKE_scene_check_color_management_enabled(CTX_data_scene(C)); + ps->use_colormanagement = BKE_scene_check_color_management_enabled(CTX_data_scene(C)); - return; + return; } void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode) { - ProjStrokeHandle *ps_handle; - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - int i; - bool is_multi_view; - char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; - - ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); - ps_handle->scene = scene; - ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); - - /* bypass regular stroke logic */ - if ((ps_handle->brush->imagepaint_tool == PAINT_TOOL_CLONE) && - (mode == BRUSH_STROKE_INVERT)) - { - view3d_operator_needs_opengl(C); - ps_handle->is_clone_cursor_pick = true; - return ps_handle; - } - - ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); - - ps_handle->symmetry_flags = settings->imapaint.paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - ps_handle->ps_views_tot = 1 + (pow_i(2, count_bits_i(ps_handle->symmetry_flags)) - 1); - is_multi_view = (ps_handle->ps_views_tot != 1); - - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); - ps_handle->ps_views[i] = ps; - } - - if (ps_handle->symmetry_flags) { - int index = 0; - - int x = 0; - do { - int y = 0; - do { - int z = 0; - do { - symmetry_flag_views[index++] = ( - (x ? PAINT_SYMM_X : 0) | - (y ? PAINT_SYMM_Y : 0) | - (z ? PAINT_SYMM_Z : 0)); - BLI_assert(index <= ps_handle->ps_views_tot); - } while ((z++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Z)); - } while ((y++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Y)); - } while ((x++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_X)); - BLI_assert(index == ps_handle->ps_views_tot); - } - - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - - project_state_init(C, ob, ps, mode); - - if (ps->ob == NULL) { - ps_handle->ps_views_tot = i + 1; - goto fail; - } - } - - /* Don't allow brush size below 2 */ - if (BKE_brush_size_get(scene, ps_handle->brush) < 2) - BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize); - - /* allocate and initialize spatial data structures */ - - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - - ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; - project_image_refresh_tagged(ps); - - /* re-use! */ - if (i != 0) { - ps->is_shared_user = true; - PROJ_PAINT_STATE_SHARED_MEMCPY(ps, ps_handle->ps_views[0]); - } - - project_paint_begin(C, ps, is_multi_view, symmetry_flag_views[i]); - if (ps->me_eval == NULL) { - goto fail; - } - - paint_proj_begin_clone(ps, mouse); - } - - paint_brush_init_tex(ps_handle->brush); - - return ps_handle; - + ProjStrokeHandle *ps_handle; + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + int i; + bool is_multi_view; + char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; + + ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); + ps_handle->scene = scene; + ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); + + /* bypass regular stroke logic */ + if ((ps_handle->brush->imagepaint_tool == PAINT_TOOL_CLONE) && (mode == BRUSH_STROKE_INVERT)) { + view3d_operator_needs_opengl(C); + ps_handle->is_clone_cursor_pick = true; + return ps_handle; + } + + ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); + + ps_handle->symmetry_flags = settings->imapaint.paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + ps_handle->ps_views_tot = 1 + (pow_i(2, count_bits_i(ps_handle->symmetry_flags)) - 1); + is_multi_view = (ps_handle->ps_views_tot != 1); + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); + ps_handle->ps_views[i] = ps; + } + + if (ps_handle->symmetry_flags) { + int index = 0; + + int x = 0; + do { + int y = 0; + do { + int z = 0; + do { + symmetry_flag_views[index++] = ((x ? PAINT_SYMM_X : 0) | (y ? PAINT_SYMM_Y : 0) | + (z ? PAINT_SYMM_Z : 0)); + BLI_assert(index <= ps_handle->ps_views_tot); + } while ((z++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Z)); + } while ((y++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Y)); + } while ((x++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_X)); + BLI_assert(index == ps_handle->ps_views_tot); + } + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + + project_state_init(C, ob, ps, mode); + + if (ps->ob == NULL) { + ps_handle->ps_views_tot = i + 1; + goto fail; + } + } + + /* Don't allow brush size below 2 */ + if (BKE_brush_size_get(scene, ps_handle->brush) < 2) + BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize); + + /* allocate and initialize spatial data structures */ + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + + ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; + project_image_refresh_tagged(ps); + + /* re-use! */ + if (i != 0) { + ps->is_shared_user = true; + PROJ_PAINT_STATE_SHARED_MEMCPY(ps, ps_handle->ps_views[0]); + } + + project_paint_begin(C, ps, is_multi_view, symmetry_flag_views[i]); + if (ps->me_eval == NULL) { + goto fail; + } + + paint_proj_begin_clone(ps, mouse); + } + + paint_brush_init_tex(ps_handle->brush); + + return ps_handle; fail: - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps = ps_handle->ps_views[i]; - MEM_freeN(ps); - } - MEM_freeN(ps_handle); - return NULL; + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + MEM_freeN(ps); + } + MEM_freeN(ps_handle); + return NULL; } void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final) { - ProjStrokeHandle *ps_handle = ps_handle_p; - - if (ps_handle->need_redraw) { - ps_handle->need_redraw = false; - } - else if (!final) { - return; - } - - if (final) { - /* compositor listener deals with updating */ - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL); - } - else { - ED_region_tag_redraw(CTX_wm_region(C)); - } + ProjStrokeHandle *ps_handle = ps_handle_p; + + if (ps_handle->need_redraw) { + ps_handle->need_redraw = false; + } + else if (!final) { + return; + } + + if (final) { + /* compositor listener deals with updating */ + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, NULL); + } + else { + ED_region_tag_redraw(CTX_wm_region(C)); + } } void paint_proj_stroke_done(void *ps_handle_p) { - ProjStrokeHandle *ps_handle = ps_handle_p; - Scene *scene = ps_handle->scene; - int i; + ProjStrokeHandle *ps_handle = ps_handle_p; + Scene *scene = ps_handle->scene; + int i; - if (ps_handle->is_clone_cursor_pick) { - MEM_freeN(ps_handle); - return; - } + if (ps_handle->is_clone_cursor_pick) { + MEM_freeN(ps_handle); + return; + } - for (i = 1; i < ps_handle->ps_views_tot; i++) { - PROJ_PAINT_STATE_SHARED_CLEAR(ps_handle->ps_views[i]); - } + for (i = 1; i < ps_handle->ps_views_tot; i++) { + PROJ_PAINT_STATE_SHARED_CLEAR(ps_handle->ps_views[i]); + } - BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size); + BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size); - paint_brush_exit_tex(ps_handle->brush); + paint_brush_exit_tex(ps_handle->brush); - for (i = 0; i < ps_handle->ps_views_tot; i++) { - ProjPaintState *ps; - ps = ps_handle->ps_views[i]; - project_paint_end(ps); - MEM_freeN(ps); + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps; + ps = ps_handle->ps_views[i]; + project_paint_end(ps); + MEM_freeN(ps); + } - } - - MEM_freeN(ps_handle); + MEM_freeN(ps_handle); } /* use project paint to re-apply an image */ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) { - Image *image = BLI_findlink(&CTX_data_main(C)->images, RNA_enum_get(op->ptr, "image")); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - ProjPaintState ps = {NULL}; - int orig_brush_size; - IDProperty *idgroup; - IDProperty *view_data = NULL; - Object *ob = OBACT(view_layer); - bool uvs, mat, tex; - - if (ob == NULL || ob->type != OB_MESH) { - BKE_report(op->reports, RPT_ERROR, "No active mesh object"); - return OPERATOR_CANCELLED; - } - - if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, NULL)) { - BKE_paint_data_warning(op->reports, uvs, mat, tex, true); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - return OPERATOR_CANCELLED; - } - - project_state_init(C, ob, &ps, BRUSH_STROKE_NORMAL); - - if (image == NULL) { - BKE_report(op->reports, RPT_ERROR, "Image could not be found"); - return OPERATOR_CANCELLED; - } - - ps.reproject_image = image; - ps.reproject_ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - - if ((ps.reproject_ibuf == NULL) || - ((ps.reproject_ibuf->rect || ps.reproject_ibuf->rect_float) == false)) - { - BKE_report(op->reports, RPT_ERROR, "Image data could not be found"); - return OPERATOR_CANCELLED; - } - - idgroup = IDP_GetProperties(&image->id, 0); - - if (idgroup) { - view_data = IDP_GetPropertyTypeFromGroup(idgroup, PROJ_VIEW_DATA_ID, IDP_ARRAY); - - /* type check to make sure its ok */ - if (view_data->len != PROJ_VIEW_DATA_SIZE || view_data->subtype != IDP_FLOAT) { - BKE_report(op->reports, RPT_ERROR, "Image project data invalid"); - return OPERATOR_CANCELLED; - } - } - - if (view_data) { - /* image has stored view projection info */ - ps.source = PROJ_SRC_IMAGE_VIEW; - } - else { - ps.source = PROJ_SRC_IMAGE_CAM; - - if (scene->camera == NULL) { - BKE_report(op->reports, RPT_ERROR, "No active camera set"); - return OPERATOR_CANCELLED; - } - } - - /* override */ - ps.is_texbrush = false; - ps.is_maskbrush = false; - ps.do_masking = false; - orig_brush_size = BKE_brush_size_get(scene, ps.brush); - /* cover the whole image */ - BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize); - - /* so pixels are initialized with minimal info */ - ps.tool = PAINT_TOOL_DRAW; - - scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; - - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_3D); - - /* allocate and initialize spatial data structures */ - project_paint_begin(C, &ps, false, 0); - - if (ps.me_eval == NULL) { - BKE_brush_size_set(scene, ps.brush, orig_brush_size); - BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh"); - return OPERATOR_CANCELLED; - } - else { - float pos[2] = {0.0, 0.0}; - float lastpos[2] = {0.0, 0.0}; - int a; - - project_paint_op(&ps, lastpos, pos); - - project_image_refresh_tagged(&ps); - - for (a = 0; a < ps.image_tot; a++) { - GPU_free_image(ps.projImages[a].ima); - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima); - } - } - - project_paint_end(&ps); - - scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - BKE_brush_size_set(scene, ps.brush, orig_brush_size); - - return OPERATOR_FINISHED; + Image *image = BLI_findlink(&CTX_data_main(C)->images, RNA_enum_get(op->ptr, "image")); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + ProjPaintState ps = {NULL}; + int orig_brush_size; + IDProperty *idgroup; + IDProperty *view_data = NULL; + Object *ob = OBACT(view_layer); + bool uvs, mat, tex; + + if (ob == NULL || ob->type != OB_MESH) { + BKE_report(op->reports, RPT_ERROR, "No active mesh object"); + return OPERATOR_CANCELLED; + } + + if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, NULL)) { + BKE_paint_data_warning(op->reports, uvs, mat, tex, true); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + return OPERATOR_CANCELLED; + } + + project_state_init(C, ob, &ps, BRUSH_STROKE_NORMAL); + + if (image == NULL) { + BKE_report(op->reports, RPT_ERROR, "Image could not be found"); + return OPERATOR_CANCELLED; + } + + ps.reproject_image = image; + ps.reproject_ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + + if ((ps.reproject_ibuf == NULL) || + ((ps.reproject_ibuf->rect || ps.reproject_ibuf->rect_float) == false)) { + BKE_report(op->reports, RPT_ERROR, "Image data could not be found"); + return OPERATOR_CANCELLED; + } + + idgroup = IDP_GetProperties(&image->id, 0); + + if (idgroup) { + view_data = IDP_GetPropertyTypeFromGroup(idgroup, PROJ_VIEW_DATA_ID, IDP_ARRAY); + + /* type check to make sure its ok */ + if (view_data->len != PROJ_VIEW_DATA_SIZE || view_data->subtype != IDP_FLOAT) { + BKE_report(op->reports, RPT_ERROR, "Image project data invalid"); + return OPERATOR_CANCELLED; + } + } + + if (view_data) { + /* image has stored view projection info */ + ps.source = PROJ_SRC_IMAGE_VIEW; + } + else { + ps.source = PROJ_SRC_IMAGE_CAM; + + if (scene->camera == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active camera set"); + return OPERATOR_CANCELLED; + } + } + + /* override */ + ps.is_texbrush = false; + ps.is_maskbrush = false; + ps.do_masking = false; + orig_brush_size = BKE_brush_size_get(scene, ps.brush); + /* cover the whole image */ + BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize); + + /* so pixels are initialized with minimal info */ + ps.tool = PAINT_TOOL_DRAW; + + scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; + + ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_3D); + + /* allocate and initialize spatial data structures */ + project_paint_begin(C, &ps, false, 0); + + if (ps.me_eval == NULL) { + BKE_brush_size_set(scene, ps.brush, orig_brush_size); + BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh"); + return OPERATOR_CANCELLED; + } + else { + float pos[2] = {0.0, 0.0}; + float lastpos[2] = {0.0, 0.0}; + int a; + + project_paint_op(&ps, lastpos, pos); + + project_image_refresh_tagged(&ps); + + for (a = 0; a < ps.image_tot; a++) { + GPU_free_image(ps.projImages[a].ima); + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ps.projImages[a].ima); + } + } + + project_paint_end(&ps); + + scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + BKE_brush_size_set(scene, ps.brush, orig_brush_size); + + return OPERATOR_FINISHED; } void PAINT_OT_project_image(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Project Image"; - ot->idname = "PAINT_OT_project_image"; - ot->description = "Project an edited render from the active camera back onto the object"; + /* identifiers */ + ot->name = "Project Image"; + ot->idname = "PAINT_OT_project_image"; + ot->description = "Project an edited render from the active camera back onto the object"; - /* api callbacks */ - ot->invoke = WM_enum_search_invoke; - ot->exec = texture_paint_camera_project_exec; + /* api callbacks */ + ot->invoke = WM_enum_search_invoke; + ot->exec = texture_paint_camera_project_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_enum(ot->srna, "image", DummyRNA_NULL_items, 0, "Image", ""); - RNA_def_enum_funcs(prop, RNA_image_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; + prop = RNA_def_enum(ot->srna, "image", DummyRNA_NULL_items, 0, "Image", ""); + RNA_def_enum_funcs(prop, RNA_image_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; } static bool texture_paint_image_from_view_poll(bContext *C) { - if (BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0) == NULL) { - CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); - return false; - } - return true; + if (BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0) == NULL) { + CTX_wm_operator_poll_msg_set(C, "No 3D viewport found to create image from"); + return false; + } + return true; } static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) { - Image *image; - ImBuf *ibuf; - char filename[FILE_MAX]; - - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - int w = settings->imapaint.screen_grab_size[0]; - int h = settings->imapaint.screen_grab_size[1]; - int maxsize; - char err_out[256] = "unknown"; - - ScrArea *sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0); - if (!sa) { - BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); - return OPERATOR_CANCELLED; - } - View3D *v3d = sa->spacedata.first; - ARegion *ar = BKE_area_find_region_active_win(sa); - if (!ar) { - BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); - return OPERATOR_CANCELLED; - } - RegionView3D *rv3d = ar->regiondata; - - RNA_string_get(op->ptr, "filepath", filename); - - maxsize = GPU_max_texture_size(); - - if (w > maxsize) w = maxsize; - if (h > maxsize) h = maxsize; - - ibuf = ED_view3d_draw_offscreen_imbuf( - depsgraph, scene, v3d->shading.type, - v3d, ar, - w, h, IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, 0, NULL, - NULL, err_out); - if (!ibuf) { - /* Mostly happens when OpenGL offscreen buffer was failed to create, */ - /* but could be other reasons. Should be handled in the future. nazgul */ - BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer: %s", err_out); - return OPERATOR_CANCELLED; - } - - image = BKE_image_add_from_imbuf(bmain, ibuf, "image_view"); - - /* Drop reference to ibuf so that the image owns it */ - IMB_freeImBuf(ibuf); - - if (image) { - /* now for the trickiness. store the view projection here! - * re-projection will reuse this */ - IDPropertyTemplate val; - IDProperty *idgroup = IDP_GetProperties(&image->id, 1); - IDProperty *view_data; - bool is_ortho; - float *array; - - val.array.len = PROJ_VIEW_DATA_SIZE; - val.array.type = IDP_FLOAT; - view_data = IDP_New(IDP_ARRAY, &val, PROJ_VIEW_DATA_ID); - - array = (float *)IDP_Array(view_data); - memcpy(array, rv3d->winmat, sizeof(rv3d->winmat)); array += sizeof(rv3d->winmat) / sizeof(float); - memcpy(array, rv3d->viewmat, sizeof(rv3d->viewmat)); array += sizeof(rv3d->viewmat) / sizeof(float); - is_ortho = ED_view3d_clip_range_get(CTX_data_depsgraph(C), v3d, rv3d, &array[0], &array[1], true); - /* using float for a bool is dodgy but since its an extra member in the array... - * easier then adding a single bool prop */ - array[2] = is_ortho ? 1.0f : 0.0f; - - IDP_AddToGroup(idgroup, view_data); - } - - return OPERATOR_FINISHED; + Image *image; + ImBuf *ibuf; + char filename[FILE_MAX]; + + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + int w = settings->imapaint.screen_grab_size[0]; + int h = settings->imapaint.screen_grab_size[1]; + int maxsize; + char err_out[256] = "unknown"; + + ScrArea *sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_VIEW3D, 0); + if (!sa) { + BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); + return OPERATOR_CANCELLED; + } + View3D *v3d = sa->spacedata.first; + ARegion *ar = BKE_area_find_region_active_win(sa); + if (!ar) { + BKE_report(op->reports, RPT_ERROR, "No 3D viewport found to create image from"); + return OPERATOR_CANCELLED; + } + RegionView3D *rv3d = ar->regiondata; + + RNA_string_get(op->ptr, "filepath", filename); + + maxsize = GPU_max_texture_size(); + + if (w > maxsize) + w = maxsize; + if (h > maxsize) + h = maxsize; + + ibuf = ED_view3d_draw_offscreen_imbuf(depsgraph, + scene, + v3d->shading.type, + v3d, + ar, + w, + h, + IB_rect, + V3D_OFSDRAW_NONE, + R_ALPHAPREMUL, + 0, + NULL, + NULL, + err_out); + if (!ibuf) { + /* Mostly happens when OpenGL offscreen buffer was failed to create, */ + /* but could be other reasons. Should be handled in the future. nazgul */ + BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer: %s", err_out); + return OPERATOR_CANCELLED; + } + + image = BKE_image_add_from_imbuf(bmain, ibuf, "image_view"); + + /* Drop reference to ibuf so that the image owns it */ + IMB_freeImBuf(ibuf); + + if (image) { + /* now for the trickiness. store the view projection here! + * re-projection will reuse this */ + IDPropertyTemplate val; + IDProperty *idgroup = IDP_GetProperties(&image->id, 1); + IDProperty *view_data; + bool is_ortho; + float *array; + + val.array.len = PROJ_VIEW_DATA_SIZE; + val.array.type = IDP_FLOAT; + view_data = IDP_New(IDP_ARRAY, &val, PROJ_VIEW_DATA_ID); + + array = (float *)IDP_Array(view_data); + memcpy(array, rv3d->winmat, sizeof(rv3d->winmat)); + array += sizeof(rv3d->winmat) / sizeof(float); + memcpy(array, rv3d->viewmat, sizeof(rv3d->viewmat)); + array += sizeof(rv3d->viewmat) / sizeof(float); + is_ortho = ED_view3d_clip_range_get( + CTX_data_depsgraph(C), v3d, rv3d, &array[0], &array[1], true); + /* using float for a bool is dodgy but since its an extra member in the array... + * easier then adding a single bool prop */ + array[2] = is_ortho ? 1.0f : 0.0f; + + IDP_AddToGroup(idgroup, view_data); + } + + return OPERATOR_FINISHED; } void PAINT_OT_image_from_view(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Image from View"; - ot->idname = "PAINT_OT_image_from_view"; - ot->description = "Make an image from biggest 3D view for re-projection"; + /* identifiers */ + ot->name = "Image from View"; + ot->idname = "PAINT_OT_image_from_view"; + ot->description = "Make an image from biggest 3D view for re-projection"; - /* api callbacks */ - ot->exec = texture_paint_image_from_view_exec; - ot->poll = texture_paint_image_from_view_poll; + /* api callbacks */ + ot->exec = texture_paint_image_from_view_exec; + ot->poll = texture_paint_image_from_view_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER; + /* flags */ + ot->flag = OPTYPE_REGISTER; - RNA_def_string_file_name(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Name of the file"); + RNA_def_string_file_name(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Name of the file"); } /********************************************* @@ -5953,462 +6158,476 @@ void PAINT_OT_image_from_view(wmOperatorType *ot) void BKE_paint_data_warning(struct ReportList *reports, bool uvs, bool mat, bool tex, bool stencil) { - BKE_reportf( - reports, RPT_WARNING, "Missing%s%s%s%s detected!", - !uvs ? " UVs," : "", - !mat ? " Materials," : "", - !tex ? " Textures," : "", - !stencil ? " Stencil," : "" - ); + BKE_reportf(reports, + RPT_WARNING, + "Missing%s%s%s%s detected!", + !uvs ? " UVs," : "", + !mat ? " Materials," : "", + !tex ? " Textures," : "", + !stencil ? " Stencil," : ""); } /* Make sure that active object has a material, * and assign UVs and image layers if they do not exist */ -bool BKE_paint_proj_mesh_data_check(Scene *scene, Object *ob, bool *uvs, bool *mat, bool *tex, bool *stencil) +bool BKE_paint_proj_mesh_data_check( + Scene *scene, Object *ob, bool *uvs, bool *mat, bool *tex, bool *stencil) { - Mesh *me; - int layernum; - ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; - Brush *br = BKE_paint_brush(&imapaint->paint); - bool hasmat = true; - bool hastex = true; - bool hasstencil = true; - bool hasuvs = true; - - imapaint->missing_data = 0; - - BLI_assert(ob->type == OB_MESH); - - if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { - /* no material, add one */ - if (ob->totcol == 0) { - hasmat = false; - hastex = false; - } - else { - /* there may be material slots but they may be empty, check */ - int i; - hasmat = false; - hastex = false; - - for (i = 1; i < ob->totcol + 1; i++) { - Material *ma = give_current_material(ob, i); - - if (ma) { - hasmat = true; - if (!ma->texpaintslot) { - /* refresh here just in case */ - BKE_texpaint_slot_refresh_cache(scene, ma); - - /* if still no slots, we have to add */ - if (ma->texpaintslot) { - hastex = true; - break; - } - } - else { - hastex = true; - break; - } - } - } - } - } - else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { - if (imapaint->canvas == NULL) { - hastex = false; - } - } - - me = BKE_mesh_from_object(ob); - layernum = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - - if (layernum == 0) { - hasuvs = false; - } - - /* Make sure we have a stencil to paint on! */ - if (br && br->imagepaint_tool == PAINT_TOOL_MASK) { - imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; - - if (imapaint->stencil == NULL) { - hasstencil = false; - } - } - - if (!hasuvs) imapaint->missing_data |= IMAGEPAINT_MISSING_UVS; - if (!hasmat) imapaint->missing_data |= IMAGEPAINT_MISSING_MATERIAL; - if (!hastex) imapaint->missing_data |= IMAGEPAINT_MISSING_TEX; - if (!hasstencil) imapaint->missing_data |= IMAGEPAINT_MISSING_STENCIL; - - if (uvs) { - *uvs = hasuvs; - } - if (mat) { - *mat = hasmat; - } - if (tex) { - *tex = hastex; - } - if (stencil) { - *stencil = hasstencil; - } - - return hasuvs && hasmat && hastex && hasstencil; + Mesh *me; + int layernum; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + Brush *br = BKE_paint_brush(&imapaint->paint); + bool hasmat = true; + bool hastex = true; + bool hasstencil = true; + bool hasuvs = true; + + imapaint->missing_data = 0; + + BLI_assert(ob->type == OB_MESH); + + if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + /* no material, add one */ + if (ob->totcol == 0) { + hasmat = false; + hastex = false; + } + else { + /* there may be material slots but they may be empty, check */ + int i; + hasmat = false; + hastex = false; + + for (i = 1; i < ob->totcol + 1; i++) { + Material *ma = give_current_material(ob, i); + + if (ma) { + hasmat = true; + if (!ma->texpaintslot) { + /* refresh here just in case */ + BKE_texpaint_slot_refresh_cache(scene, ma); + + /* if still no slots, we have to add */ + if (ma->texpaintslot) { + hastex = true; + break; + } + } + else { + hastex = true; + break; + } + } + } + } + } + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { + if (imapaint->canvas == NULL) { + hastex = false; + } + } + + me = BKE_mesh_from_object(ob); + layernum = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + + if (layernum == 0) { + hasuvs = false; + } + + /* Make sure we have a stencil to paint on! */ + if (br && br->imagepaint_tool == PAINT_TOOL_MASK) { + imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; + + if (imapaint->stencil == NULL) { + hasstencil = false; + } + } + + if (!hasuvs) + imapaint->missing_data |= IMAGEPAINT_MISSING_UVS; + if (!hasmat) + imapaint->missing_data |= IMAGEPAINT_MISSING_MATERIAL; + if (!hastex) + imapaint->missing_data |= IMAGEPAINT_MISSING_TEX; + if (!hasstencil) + imapaint->missing_data |= IMAGEPAINT_MISSING_STENCIL; + + if (uvs) { + *uvs = hasuvs; + } + if (mat) { + *mat = hasmat; + } + if (tex) { + *tex = hastex; + } + if (stencil) { + *stencil = hasstencil; + } + + return hasuvs && hasmat && hastex && hasstencil; } /* Add layer operator */ enum { - LAYER_BASE_COLOR, - LAYER_SPECULAR, - LAYER_ROUGHNESS, - LAYER_METALLIC, - LAYER_NORMAL, - LAYER_BUMP, - LAYER_DISPLACEMENT, + LAYER_BASE_COLOR, + LAYER_SPECULAR, + LAYER_ROUGHNESS, + LAYER_METALLIC, + LAYER_NORMAL, + LAYER_BUMP, + LAYER_DISPLACEMENT, }; static const EnumPropertyItem layer_type_items[] = { - {LAYER_BASE_COLOR, "BASE_COLOR", 0, "Base Color", ""}, - {LAYER_SPECULAR, "SPECULAR", 0, "Specular", ""}, - {LAYER_ROUGHNESS, "ROUGHNESS", 0, "Roughness", ""}, - {LAYER_METALLIC, "METALLIC", 0, "Metallic", ""}, - {LAYER_NORMAL, "NORMAL", 0, "Normal", ""}, - {LAYER_BUMP, "BUMP", 0, "Bump", ""}, - {LAYER_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", ""}, - {0, NULL, 0, NULL, NULL}, + {LAYER_BASE_COLOR, "BASE_COLOR", 0, "Base Color", ""}, + {LAYER_SPECULAR, "SPECULAR", 0, "Specular", ""}, + {LAYER_ROUGHNESS, "ROUGHNESS", 0, "Roughness", ""}, + {LAYER_METALLIC, "METALLIC", 0, "Metallic", ""}, + {LAYER_NORMAL, "NORMAL", 0, "Normal", ""}, + {LAYER_BUMP, "BUMP", 0, "Bump", ""}, + {LAYER_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", ""}, + {0, NULL, 0, NULL, NULL}, }; static Image *proj_paint_image_create(wmOperator *op, Main *bmain) { - Image *ima; - float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - char imagename[MAX_ID_NAME - 2] = "Material Diffuse Color"; - int width = 1024; - int height = 1024; - bool use_float = false; - short gen_type = IMA_GENTYPE_BLANK; - bool alpha = false; - - if (op) { - width = RNA_int_get(op->ptr, "width"); - height = RNA_int_get(op->ptr, "height"); - use_float = RNA_boolean_get(op->ptr, "float"); - gen_type = RNA_enum_get(op->ptr, "generated_type"); - RNA_float_get_array(op->ptr, "color", color); - alpha = RNA_boolean_get(op->ptr, "alpha"); - RNA_string_get(op->ptr, "name", imagename); - } - ima = BKE_image_add_generated( - bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color, false); - - return ima; + Image *ima; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + char imagename[MAX_ID_NAME - 2] = "Material Diffuse Color"; + int width = 1024; + int height = 1024; + bool use_float = false; + short gen_type = IMA_GENTYPE_BLANK; + bool alpha = false; + + if (op) { + width = RNA_int_get(op->ptr, "width"); + height = RNA_int_get(op->ptr, "height"); + use_float = RNA_boolean_get(op->ptr, "float"); + gen_type = RNA_enum_get(op->ptr, "generated_type"); + RNA_float_get_array(op->ptr, "color", color); + alpha = RNA_boolean_get(op->ptr, "alpha"); + RNA_string_get(op->ptr, "name", imagename); + } + ima = BKE_image_add_generated( + bmain, width, height, imagename, alpha ? 32 : 24, use_float, gen_type, color, false); + + return ima; } static void proj_paint_default_color(wmOperator *op, int type, Material *ma) { - if (RNA_struct_property_is_set(op->ptr, "color")) { - return; - } - - bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED); - if (in_node == NULL) { - return; - } - - float color[4]; - - if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { - /* Copy color from node, so result is unchanged after assigning textures. */ - bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - - switch (in_sock->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *socket_data = in_sock->default_value; - copy_v3_fl(color, socket_data->value); - color[3] = 1.0f; - break; - } - case SOCK_VECTOR: - case SOCK_RGBA: { - bNodeSocketValueRGBA *socket_data = in_sock->default_value; - copy_v3_v3(color, socket_data->value); - color[3] = 1.0f; - break; - } - default: { - return; - } - } - } - else if (type == LAYER_NORMAL) { - /* Neutral tangent space normal map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); - } - else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) { - /* Neutral displacement and bump map. */ - rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); - } - else { - return; - } - - RNA_float_set_array(op->ptr, "color", color); + if (RNA_struct_property_is_set(op->ptr, "color")) { + return; + } + + bNode *in_node = ntreeFindType(ma->nodetree, SH_NODE_BSDF_PRINCIPLED); + if (in_node == NULL) { + return; + } + + float color[4]; + + if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { + /* Copy color from node, so result is unchanged after assigning textures. */ + bNodeSocket *in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + + switch (in_sock->type) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *socket_data = in_sock->default_value; + copy_v3_fl(color, socket_data->value); + color[3] = 1.0f; + break; + } + case SOCK_VECTOR: + case SOCK_RGBA: { + bNodeSocketValueRGBA *socket_data = in_sock->default_value; + copy_v3_v3(color, socket_data->value); + color[3] = 1.0f; + break; + } + default: { + return; + } + } + } + else if (type == LAYER_NORMAL) { + /* Neutral tangent space normal map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 1.0f, 1.0f); + } + else if (ELEM(type, LAYER_BUMP, LAYER_DISPLACEMENT)) { + /* Neutral displacement and bump map. */ + rgba_float_args_set(color, 0.5f, 0.5f, 0.5f, 1.0f); + } + else { + return; + } + + RNA_float_set_array(op->ptr, "color", color); } static bool proj_paint_add_slot(bContext *C, wmOperator *op) { - Object *ob = ED_object_active_context(C); - Scene *scene = CTX_data_scene(C); - Material *ma; - Image *ima = NULL; - - if (!ob) - return false; - - ma = give_current_material(ob, ob->actcol); + Object *ob = ED_object_active_context(C); + Scene *scene = CTX_data_scene(C); + Material *ma; + Image *ima = NULL; + + if (!ob) + return false; + + ma = give_current_material(ob, ob->actcol); - if (ma) { - Main *bmain = CTX_data_main(C); - int type = RNA_enum_get(op->ptr, "type"); + if (ma) { + Main *bmain = CTX_data_main(C); + int type = RNA_enum_get(op->ptr, "type"); - bNode *imanode; - bNodeTree *ntree = ma->nodetree; + bNode *imanode; + bNodeTree *ntree = ma->nodetree; - if (!ntree) { - ED_node_shader_default(C, &ma->id); - ntree = ma->nodetree; - } + if (!ntree) { + ED_node_shader_default(C, &ma->id); + ntree = ma->nodetree; + } - ma->use_nodes = true; + ma->use_nodes = true; - /* try to add an image node */ - imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + /* try to add an image node */ + imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - ima = proj_paint_image_create(op, bmain); - imanode->id = &ima->id; + ima = proj_paint_image_create(op, bmain); + imanode->id = &ima->id; - nodeSetActive(ntree, imanode); + nodeSetActive(ntree, imanode); - /* Connect to first available principled bsdf node. */ - bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - bNode *out_node = imanode; + /* Connect to first available principled bsdf node. */ + bNode *in_node = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); + bNode *out_node = imanode; - if (in_node != NULL) { - bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); - bNodeSocket *in_sock = NULL; + if (in_node != NULL) { + bNodeSocket *out_sock = nodeFindSocket(out_node, SOCK_OUT, "Color"); + bNodeSocket *in_sock = NULL; - if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { - in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - } - else if (type == LAYER_NORMAL) { - bNode *nor_node; - nor_node = nodeAddStaticNode(C, ntree, SH_NODE_NORMAL_MAP); + if (type >= LAYER_BASE_COLOR && type < LAYER_NORMAL) { + in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + } + else if (type == LAYER_NORMAL) { + bNode *nor_node; + nor_node = nodeAddStaticNode(C, ntree, SH_NODE_NORMAL_MAP); - in_sock = nodeFindSocket(nor_node, SOCK_IN, "Color"); - nodeAddLink(ntree, out_node, out_sock, nor_node, in_sock); + in_sock = nodeFindSocket(nor_node, SOCK_IN, "Color"); + nodeAddLink(ntree, out_node, out_sock, nor_node, in_sock); - in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); - out_sock = nodeFindSocket(nor_node, SOCK_OUT, "Normal"); + in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); + out_sock = nodeFindSocket(nor_node, SOCK_OUT, "Normal"); - out_node = nor_node; - } - else if (type == LAYER_BUMP) { - bNode *bump_node; - bump_node = nodeAddStaticNode(C, ntree, SH_NODE_BUMP); + out_node = nor_node; + } + else if (type == LAYER_BUMP) { + bNode *bump_node; + bump_node = nodeAddStaticNode(C, ntree, SH_NODE_BUMP); - in_sock = nodeFindSocket(bump_node, SOCK_IN, "Height"); - nodeAddLink(ntree, out_node, out_sock, bump_node, in_sock); + in_sock = nodeFindSocket(bump_node, SOCK_IN, "Height"); + nodeAddLink(ntree, out_node, out_sock, bump_node, in_sock); - in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); - out_sock = nodeFindSocket(bump_node, SOCK_OUT, "Normal"); + in_sock = nodeFindSocket(in_node, SOCK_IN, "Normal"); + out_sock = nodeFindSocket(bump_node, SOCK_OUT, "Normal"); - out_node = bump_node; - } - else if (type == LAYER_DISPLACEMENT) { - /* Connect to the displacement output socket */ - in_node = ntreeFindType(ntree, SH_NODE_OUTPUT_MATERIAL); + out_node = bump_node; + } + else if (type == LAYER_DISPLACEMENT) { + /* Connect to the displacement output socket */ + in_node = ntreeFindType(ntree, SH_NODE_OUTPUT_MATERIAL); - if (in_node != NULL) { - in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); - } - else { - in_sock = NULL; - } - } + if (in_node != NULL) { + in_sock = nodeFindSocket(in_node, SOCK_IN, layer_type_items[type].name); + } + else { + in_sock = NULL; + } + } - if (type > LAYER_BASE_COLOR) { - /* This is a "non color data" image */ - NodeTexImage *tex = imanode->storage; - tex->color_space = SHD_COLORSPACE_NONE; - } + if (type > LAYER_BASE_COLOR) { + /* This is a "non color data" image */ + NodeTexImage *tex = imanode->storage; + tex->color_space = SHD_COLORSPACE_NONE; + } - /* Check if the socket in already connected to something */ - bNodeLink *link = in_sock ? in_sock->link : NULL; - if (in_sock != NULL && link == NULL) { - nodeAddLink(ntree, out_node, out_sock, in_node, in_sock); + /* Check if the socket in already connected to something */ + bNodeLink *link = in_sock ? in_sock->link : NULL; + if (in_sock != NULL && link == NULL) { + nodeAddLink(ntree, out_node, out_sock, in_node, in_sock); - nodePositionRelative(out_node, in_node, out_sock, in_sock); - } - } + nodePositionRelative(out_node, in_node, out_sock, in_sock); + } + } - ntreeUpdateTree(CTX_data_main(C), ntree); - /* In case we added more than one node, position them too. */ - nodePositionPropagate(out_node); + ntreeUpdateTree(CTX_data_main(C), ntree); + /* In case we added more than one node, position them too. */ + nodePositionPropagate(out_node); - if (ima) { - BKE_texpaint_slot_refresh_cache(scene, ma); - BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); - WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); - } + if (ima) { + BKE_texpaint_slot_refresh_cache(scene, ma); + BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); + WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); + } - DEG_id_tag_update(&ntree->id, 0); - DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); - ED_area_tag_redraw(CTX_wm_area(C)); + DEG_id_tag_update(&ntree->id, 0); + DEG_id_tag_update(&ma->id, ID_RECALC_SHADING); + ED_area_tag_redraw(CTX_wm_area(C)); - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - return true; - } + return true; + } - return false; + return false; } static int get_texture_layer_type(wmOperator *op, const char *prop_name) { - int type_value = RNA_enum_get(op->ptr, prop_name); - int type = RNA_enum_from_value(layer_type_items, type_value); - BLI_assert(type != -1); - return type; + int type_value = RNA_enum_get(op->ptr, prop_name); + int type = RNA_enum_from_value(layer_type_items, type_value); + BLI_assert(type != -1); + return type; } static Material *get_or_create_current_material(bContext *C, Object *ob) { - Material *ma = give_current_material(ob, ob->actcol); - if (!ma) { - Main *bmain = CTX_data_main(C); - ma = BKE_material_add(bmain, "Material"); - assign_material(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); - } - return ma; + Material *ma = give_current_material(ob, ob->actcol); + if (!ma) { + Main *bmain = CTX_data_main(C); + ma = BKE_material_add(bmain, "Material"); + assign_material(bmain, ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); + } + return ma; } static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op) { - Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); - - int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); - - if (proj_paint_add_slot(C, op)) { - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *ob = ED_object_active_context(C); + Material *ma = get_or_create_current_material(C, ob); + + int type = get_texture_layer_type(op, "type"); + proj_paint_default_color(op, type, ma); + + if (proj_paint_add_slot(C, op)) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } -static void get_default_texture_layer_name_for_object(Object *ob, int texture_type, char *dst, int dst_length) +static void get_default_texture_layer_name_for_object(Object *ob, + int texture_type, + char *dst, + int dst_length) { - Material *ma = give_current_material(ob, ob->actcol); - const char *base_name = ma ? &ma->id.name[2] : &ob->id.name[2]; - BLI_snprintf(dst, dst_length, "%s %s", base_name, layer_type_items[texture_type].name); + Material *ma = give_current_material(ob, ob->actcol); + const char *base_name = ma ? &ma->id.name[2] : &ob->id.name[2]; + BLI_snprintf(dst, dst_length, "%s %s", base_name, layer_type_items[texture_type].name); } -static int texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int texture_paint_add_texture_paint_slot_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) { - /* Get material and default color to display in the popup. */ - Object *ob = ED_object_active_context(C); - Material *ma = get_or_create_current_material(C, ob); + /* Get material and default color to display in the popup. */ + Object *ob = ED_object_active_context(C); + Material *ma = get_or_create_current_material(C, ob); - int type = get_texture_layer_type(op, "type"); - proj_paint_default_color(op, type, ma); + int type = get_texture_layer_type(op, "type"); + proj_paint_default_color(op, type, ma); - char imagename[MAX_ID_NAME - 2]; - get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); - RNA_string_set(op->ptr, "name", imagename); + char imagename[MAX_ID_NAME - 2]; + get_default_texture_layer_name_for_object(ob, type, (char *)&imagename, sizeof(imagename)); + RNA_string_set(op->ptr, "name", imagename); - return WM_operator_props_dialog_popup(C, op, 300, 100); + return WM_operator_props_dialog_popup(C, op, 300, 100); } #define IMA_DEF_NAME N_("Untitled") - void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) { - PropertyRNA *prop; - static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - - /* identifiers */ - ot->name = "Add Texture Paint Slot"; - ot->description = "Add a texture paint slot"; - ot->idname = "PAINT_OT_add_texture_paint_slot"; - - /* api callbacks */ - ot->invoke = texture_paint_add_texture_paint_slot_invoke; - ot->exec = texture_paint_add_texture_paint_slot_exec; - ot->poll = ED_operator_object_active; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); - prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); - RNA_def_property_subtype(prop, PROP_PIXEL); - prop = RNA_def_float_color(ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); - RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); - RNA_def_property_float_array_default(prop, default_color); - RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); - RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, - "Generated Type", "Fill the image with a grid for UV map testing"); - RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); + PropertyRNA *prop; + static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + /* identifiers */ + ot->name = "Add Texture Paint Slot"; + ot->description = "Add a texture paint slot"; + ot->idname = "PAINT_OT_add_texture_paint_slot"; + + /* api callbacks */ + ot->invoke = texture_paint_add_texture_paint_slot_invoke; + ot->exec = texture_paint_add_texture_paint_slot_exec; + ot->poll = ED_operator_object_active; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name"); + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_float_color( + ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); + RNA_def_property_float_array_default(prop, default_color); + RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, + "generated_type", + rna_enum_image_generated_type_items, + IMA_GENTYPE_BLANK, + "Generated Type", + "Fill the image with a grid for UV map testing"); + RNA_def_boolean( + ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); } static int add_simple_uvs_exec(bContext *C, wmOperator *UNUSED(op)) { - /* no checks here, poll function does them for us */ - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - Scene *scene = CTX_data_scene(C); + /* no checks here, poll function does them for us */ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); - ED_uvedit_add_simple_uvs(bmain, scene, ob); + ED_uvedit_add_simple_uvs(bmain, scene, ob); - BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); - DEG_id_tag_update(ob->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, scene); - return OPERATOR_FINISHED; + DEG_id_tag_update(ob->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, scene); + return OPERATOR_FINISHED; } static bool add_simple_uvs_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - if (!ob || ob->type != OB_MESH || ob->mode != OB_MODE_TEXTURE_PAINT) { - return false; - } - return true; + if (!ob || ob->type != OB_MESH || ob->mode != OB_MODE_TEXTURE_PAINT) { + return false; + } + return true; } void PAINT_OT_add_simple_uvs(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add simple UVs"; - ot->description = "Add cube map uvs on mesh"; - ot->idname = "PAINT_OT_add_simple_uvs"; + /* identifiers */ + ot->name = "Add simple UVs"; + ot->description = "Add cube map uvs on mesh"; + ot->idname = "PAINT_OT_add_simple_uvs"; - /* api callbacks */ - ot->exec = add_simple_uvs_exec; - ot->poll = add_simple_uvs_poll; + /* api callbacks */ + ot->exec = add_simple_uvs_exec; + ot->poll = add_simple_uvs_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } diff --git a/source/blender/editors/sculpt_paint/paint_image_undo.c b/source/blender/editors/sculpt_paint/paint_image_undo.c index 8c4bfe00654..68abdfbf95b 100644 --- a/source/blender/editors/sculpt_paint/paint_image_undo.c +++ b/source/blender/editors/sculpt_paint/paint_image_undo.c @@ -55,26 +55,26 @@ * \{ */ typedef struct UndoImageTile { - struct UndoImageTile *next, *prev; + struct UndoImageTile *next, *prev; - char ibufname[IMB_FILENAME_SIZE]; + char ibufname[IMB_FILENAME_SIZE]; - union { - float *fp; - unsigned int *uint; - void *pt; - } rect; + union { + float *fp; + unsigned int *uint; + void *pt; + } rect; - unsigned short *mask; + unsigned short *mask; - int x, y; + int x, y; - Image *ima; - short source, use_float; - char gen_type; - bool valid; + Image *ima; + short source, use_float; + char gen_type; + bool valid; - size_t undo_size; + size_t undo_size; } UndoImageTile; /* this is a static resource for non-globality, @@ -84,281 +84,305 @@ static SpinLock undolock; void image_undo_init_locks(void) { - BLI_spin_init(&undolock); + BLI_spin_init(&undolock); } void image_undo_end_locks(void) { - BLI_spin_end(&undolock); + BLI_spin_end(&undolock); } /* UNDO */ typedef enum { - COPY = 0, - RESTORE = 1, - RESTORE_COPY = 2, + COPY = 0, + RESTORE = 1, + RESTORE_COPY = 2, } CopyMode; static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode) { - if (mode == COPY) { - /* copy or swap contents of tile->rect and region in ibuf->rect */ - IMB_rectcpy( - tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); - - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - } - else { - if (mode == RESTORE_COPY) { - IMB_rectcpy( - tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); - } - /* swap to the tmpbuf for easy copying */ - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - - IMB_rectcpy( - ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); - - if (mode == RESTORE) { - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); - } - else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - } - } + if (mode == COPY) { + /* copy or swap contents of tile->rect and region in ibuf->rect */ + IMB_rectcpy(tmpibuf, + ibuf, + 0, + 0, + tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, + IMAPAINT_TILE_SIZE, + IMAPAINT_TILE_SIZE); + + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + } + else { + if (mode == RESTORE_COPY) { + IMB_rectcpy(tmpibuf, + ibuf, + 0, + 0, + tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, + IMAPAINT_TILE_SIZE, + IMAPAINT_TILE_SIZE); + } + /* swap to the tmpbuf for easy copying */ + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + + IMB_rectcpy(ibuf, + tmpibuf, + tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, + 0, + 0, + IMAPAINT_TILE_SIZE, + IMAPAINT_TILE_SIZE); + + if (mode == RESTORE) { + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + } + } } -void *image_undo_find_tile( - ListBase *undo_tiles, - Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate) +void *image_undo_find_tile(ListBase *undo_tiles, + Image *ima, + ImBuf *ibuf, + int x_tile, + int y_tile, + unsigned short **mask, + bool validate) { - UndoImageTile *tile; - short use_float = ibuf->rect_float ? 1 : 0; - - for (tile = undo_tiles->first; tile; tile = tile->next) { - if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) { - if (tile->use_float == use_float) { - if (STREQ(tile->ibufname, ibuf->name)) { - if (mask) { - /* allocate mask if requested */ - if (!tile->mask) { - tile->mask = MEM_callocN( - sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, - "UndoImageTile.mask"); - } - - *mask = tile->mask; - } - if (validate) { - tile->valid = true; - } - return tile->rect.pt; - } - } - } - } - - return NULL; + UndoImageTile *tile; + short use_float = ibuf->rect_float ? 1 : 0; + + for (tile = undo_tiles->first; tile; tile = tile->next) { + if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && + ima->source == tile->source) { + if (tile->use_float == use_float) { + if (STREQ(tile->ibufname, ibuf->name)) { + if (mask) { + /* allocate mask if requested */ + if (!tile->mask) { + tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * + IMAPAINT_TILE_SIZE, + "UndoImageTile.mask"); + } + + *mask = tile->mask; + } + if (validate) { + tile->valid = true; + } + return tile->rect.pt; + } + } + } + } + + return NULL; } -void *image_undo_push_tile( - ListBase *undo_tiles, - Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, - unsigned short **mask, bool **valid, bool proj, bool find_prev) +void *image_undo_push_tile(ListBase *undo_tiles, + Image *ima, + ImBuf *ibuf, + ImBuf **tmpibuf, + int x_tile, + int y_tile, + unsigned short **mask, + bool **valid, + bool proj, + bool find_prev) { - UndoImageTile *tile; - int allocsize; - short use_float = ibuf->rect_float ? 1 : 0; - void *data; - - /* check if tile is already pushed */ - - /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ - if (find_prev) { - data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true); - if (data) { - return data; - } - } - - if (*tmpibuf == NULL) { - *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - } - - tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile"); - tile->x = x_tile; - tile->y = y_tile; - - /* add mask explicitly here */ - if (mask) { - *mask = tile->mask = MEM_callocN( - sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, - "UndoImageTile.mask"); - } - allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; - allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); - tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); - - BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname)); - - tile->gen_type = ima->gen_type; - tile->source = ima->source; - tile->use_float = use_float; - tile->valid = true; - tile->ima = ima; - - if (valid) { - *valid = &tile->valid; - } - undo_copy_tile(tile, *tmpibuf, ibuf, COPY); - - if (proj) { - BLI_spin_lock(&undolock); - } - BLI_addtail(undo_tiles, tile); - - if (proj) { - BLI_spin_unlock(&undolock); - } - return tile->rect.pt; + UndoImageTile *tile; + int allocsize; + short use_float = ibuf->rect_float ? 1 : 0; + void *data; + + /* check if tile is already pushed */ + + /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ + if (find_prev) { + data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true); + if (data) { + return data; + } + } + + if (*tmpibuf == NULL) { + *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); + } + + tile = MEM_callocN(sizeof(UndoImageTile), "UndoImageTile"); + tile->x = x_tile; + tile->y = y_tile; + + /* add mask explicitly here */ + if (mask) { + *mask = tile->mask = MEM_callocN( + sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, "UndoImageTile.mask"); + } + allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; + allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); + tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); + + BLI_strncpy(tile->ibufname, ibuf->name, sizeof(tile->ibufname)); + + tile->gen_type = ima->gen_type; + tile->source = ima->source; + tile->use_float = use_float; + tile->valid = true; + tile->ima = ima; + + if (valid) { + *valid = &tile->valid; + } + undo_copy_tile(tile, *tmpibuf, ibuf, COPY); + + if (proj) { + BLI_spin_lock(&undolock); + } + BLI_addtail(undo_tiles, tile); + + if (proj) { + BLI_spin_unlock(&undolock); + } + return tile->rect.pt; } void image_undo_remove_masks(void) { - ListBase *undo_tiles = ED_image_undo_get_tiles(); - UndoImageTile *tile; - - for (tile = undo_tiles->first; tile; tile = tile->next) { - if (tile->mask) { - MEM_freeN(tile->mask); - tile->mask = NULL; - } - } + ListBase *undo_tiles = ED_image_undo_get_tiles(); + UndoImageTile *tile; + + for (tile = undo_tiles->first; tile; tile = tile->next) { + if (tile->mask) { + MEM_freeN(tile->mask); + tile->mask = NULL; + } + } } static void image_undo_restore_runtime(ListBase *lb) { - ImBuf *ibuf, *tmpibuf; - UndoImageTile *tile; + ImBuf *ibuf, *tmpibuf; + UndoImageTile *tile; - tmpibuf = IMB_allocImBuf( - IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, - IB_rectfloat | IB_rect); + tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - for (tile = lb->first; tile; tile = tile->next) { - Image *ima = tile->ima; - ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + for (tile = lb->first; tile; tile = tile->next) { + Image *ima = tile->ima; + ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); - GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ - } - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ + if (ibuf->rect_float) { + ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ + } + if (ibuf->mipmap[0]) { + ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ + } + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - BKE_image_release_ibuf(ima, ibuf, NULL); - } + BKE_image_release_ibuf(ima, ibuf, NULL); + } - IMB_freeImBuf(tmpibuf); + IMB_freeImBuf(tmpibuf); } static void image_undo_restore_list(ListBase *lb, struct UndoIDPtrMap *id_map) { - ImBuf *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); + ImBuf *tmpibuf = IMB_allocImBuf( + IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); - /* Store last found image. */ - ID *image_prev[2] = {NULL}; + /* Store last found image. */ + ID *image_prev[2] = {NULL}; - for (UndoImageTile *tile = lb->first; tile; tile = tile->next) { - short use_float; + for (UndoImageTile *tile = lb->first; tile; tile = tile->next) { + short use_float; - Image *ima = (Image *)BKE_undosys_ID_map_lookup_with_prev(id_map, &tile->ima->id, image_prev); + Image *ima = (Image *)BKE_undosys_ID_map_lookup_with_prev(id_map, &tile->ima->id, image_prev); - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); - if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) { - /* current ImBuf filename was changed, probably current frame - * was changed when painting on image sequence, rather than storing - * full image user (which isn't so obvious, btw) try to find ImBuf with - * matched file name in list of already loaded images */ + if (ima && ibuf && !STREQ(tile->ibufname, ibuf->name)) { + /* current ImBuf filename was changed, probably current frame + * was changed when painting on image sequence, rather than storing + * full image user (which isn't so obvious, btw) try to find ImBuf with + * matched file name in list of already loaded images */ - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, NULL); - ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname); - } + ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname); + } - if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } + if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { + BKE_image_release_ibuf(ima, ibuf, NULL); + continue; + } - if (ima->gen_type != tile->gen_type || ima->source != tile->source) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } + if (ima->gen_type != tile->gen_type || ima->source != tile->source) { + BKE_image_release_ibuf(ima, ibuf, NULL); + continue; + } - use_float = ibuf->rect_float ? 1 : 0; + use_float = ibuf->rect_float ? 1 : 0; - if (use_float != tile->use_float) { - BKE_image_release_ibuf(ima, ibuf, NULL); - continue; - } + if (use_float != tile->use_float) { + BKE_image_release_ibuf(ima, ibuf, NULL); + continue; + } - undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); - GPU_free_image(ima); /* force OpenGL reload */ - if (ibuf->rect_float) { - ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ - } - if (ibuf->mipmap[0]) { - ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ - } - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + GPU_free_image(ima); /* force OpenGL reload */ + if (ibuf->rect_float) { + ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ + } + if (ibuf->mipmap[0]) { + ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ + } + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - DEG_id_tag_update(&ima->id, 0); + DEG_id_tag_update(&ima->id, 0); - BKE_image_release_ibuf(ima, ibuf, NULL); - } + BKE_image_release_ibuf(ima, ibuf, NULL); + } - IMB_freeImBuf(tmpibuf); + IMB_freeImBuf(tmpibuf); } static void image_undo_free_list(ListBase *lb) { - for (UndoImageTile *tile = lb->first, *tile_next; tile; tile = tile_next) { - tile_next = tile->next; - MEM_freeN(tile->rect.pt); - MEM_freeN(tile); - } + for (UndoImageTile *tile = lb->first, *tile_next; tile; tile = tile_next) { + tile_next = tile->next; + MEM_freeN(tile->rect.pt); + MEM_freeN(tile); + } } static void image_undo_invalidate(void) { - UndoImageTile *tile; - ListBase *lb = ED_image_undo_get_tiles(); + UndoImageTile *tile; + ListBase *lb = ED_image_undo_get_tiles(); - for (tile = lb->first; tile; tile = tile->next) { - tile->valid = false; - } + for (tile = lb->first; tile; tile = tile->next) { + tile->valid = false; + } } /** \} */ @@ -368,258 +392,260 @@ static void image_undo_invalidate(void) * \{ */ typedef struct ImageUndoStep { - UndoStep step; - ListBase tiles; - bool is_encode_init; - ePaintMode paint_mode; + UndoStep step; + ListBase tiles; + bool is_encode_init; + ePaintMode paint_mode; - /* Use for all ID lookups (can be NULL). */ - struct UndoIDPtrMap *id_map; + /* Use for all ID lookups (can be NULL). */ + struct UndoIDPtrMap *id_map; } ImageUndoStep; static void image_undosys_step_encode_store_ids(ImageUndoStep *us) { - us->id_map = BKE_undosys_ID_map_create(); + us->id_map = BKE_undosys_ID_map_create(); - ID *image_prev = NULL; - for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) { - BKE_undosys_ID_map_add_with_prev(us->id_map, &tile->ima->id, &image_prev); - } + ID *image_prev = NULL; + for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) { + BKE_undosys_ID_map_add_with_prev(us->id_map, &tile->ima->id, &image_prev); + } } /* Restore at runtime. */ #if 0 static void paint_undosys_step_decode_restore_ids(ImageUndoStep *us) { - ID *image_prev[2] = {NULL}; - for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) { - tile->ima = (Image *)BKE_undosys_ID_map_lookup_with_prev(us->id_map, &tile->ima->id, image_prev); - } + ID *image_prev[2] = {NULL}; + for (UndoImageTile *tile = us->tiles.first; tile; tile = tile->next) { + tile->ima = (Image *)BKE_undosys_ID_map_lookup_with_prev(us->id_map, &tile->ima->id, image_prev); + } } #endif static bool image_undosys_poll(bContext *C) { - Object *obact = CTX_data_active_object(C); - - ScrArea *sa = CTX_wm_area(C); - if (sa && (sa->spacetype == SPACE_IMAGE)) { - SpaceImage *sima = (SpaceImage *)sa->spacedata.first; - if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || - (sima->mode == SI_MODE_PAINT)) - { - return true; - } - } - else { - if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { - return true; - } - } - return false; + Object *obact = CTX_data_active_object(C); + + ScrArea *sa = CTX_wm_area(C); + if (sa && (sa->spacetype == SPACE_IMAGE)) { + SpaceImage *sima = (SpaceImage *)sa->spacedata.first; + if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { + return true; + } + } + else { + if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) { + return true; + } + } + return false; } static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { - ImageUndoStep *us = (ImageUndoStep *)us_p; - /* dummy, memory is cleared anyway. */ - us->is_encode_init = true; - BLI_listbase_clear(&us->tiles); + ImageUndoStep *us = (ImageUndoStep *)us_p; + /* dummy, memory is cleared anyway. */ + us->is_encode_init = true; + BLI_listbase_clear(&us->tiles); } -static bool image_undosys_step_encode(struct bContext *C, struct Main *UNUSED(bmain), UndoStep *us_p) +static bool image_undosys_step_encode(struct bContext *C, + struct Main *UNUSED(bmain), + UndoStep *us_p) { - /* dummy, encoding is done along the way by adding tiles - * to the current 'ImageUndoStep' added by encode_init. */ - ImageUndoStep *us = (ImageUndoStep *)us_p; - - BLI_assert(us->step.data_size == 0); - - int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; - - if (us->is_encode_init) { - /* first dispose of invalid tiles (may happen due to drag dot for instance) */ - for (UndoImageTile *tile = us->tiles.first; tile;) { - if (!tile->valid) { - UndoImageTile *tmp_tile = tile->next; - MEM_freeN(tile->rect.pt); - BLI_freelinkN(&us->tiles, tile); - tile = tmp_tile; - } - else { - us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); - tile = tile->next; - } - } - } - else { - /* Happens when switching modes. */ - ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); - us->paint_mode = paint_mode; - } - - image_undosys_step_encode_store_ids(us); - - us_p->is_applied = true; - - return true; + /* dummy, encoding is done along the way by adding tiles + * to the current 'ImageUndoStep' added by encode_init. */ + ImageUndoStep *us = (ImageUndoStep *)us_p; + + BLI_assert(us->step.data_size == 0); + + int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; + + if (us->is_encode_init) { + /* first dispose of invalid tiles (may happen due to drag dot for instance) */ + for (UndoImageTile *tile = us->tiles.first; tile;) { + if (!tile->valid) { + UndoImageTile *tmp_tile = tile->next; + MEM_freeN(tile->rect.pt); + BLI_freelinkN(&us->tiles, tile); + tile = tmp_tile; + } + else { + us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); + tile = tile->next; + } + } + } + else { + /* Happens when switching modes. */ + ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); + BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); + us->paint_mode = paint_mode; + } + + image_undosys_step_encode_store_ids(us); + + us_p->is_applied = true; + + return true; } - static void image_undosys_step_decode_undo_impl(ImageUndoStep *us) { - BLI_assert(us->step.is_applied == true); - image_undo_restore_list(&us->tiles, us->id_map); - us->step.is_applied = false; + BLI_assert(us->step.is_applied == true); + image_undo_restore_list(&us->tiles, us->id_map); + us->step.is_applied = false; } static void image_undosys_step_decode_redo_impl(ImageUndoStep *us) { - BLI_assert(us->step.is_applied == false); - image_undo_restore_list(&us->tiles, us->id_map); - us->step.is_applied = true; + BLI_assert(us->step.is_applied == false); + image_undo_restore_list(&us->tiles, us->id_map); + us->step.is_applied = true; } static void image_undosys_step_decode_undo(ImageUndoStep *us) { - ImageUndoStep *us_iter = us; - while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { - if (us_iter->step.next->is_applied == false) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.next; - } - while (us_iter != us) { - image_undosys_step_decode_undo_impl(us_iter); - us_iter = (ImageUndoStep *)us_iter->step.prev; - } + ImageUndoStep *us_iter = us; + while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { + if (us_iter->step.next->is_applied == false) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.next; + } + while (us_iter != us) { + image_undosys_step_decode_undo_impl(us_iter); + us_iter = (ImageUndoStep *)us_iter->step.prev; + } } static void image_undosys_step_decode_redo(ImageUndoStep *us) { - ImageUndoStep *us_iter = us; - while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { - if (us_iter->step.prev->is_applied == true) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.prev; - } - while (us_iter && (us_iter->step.is_applied == false)) { - image_undosys_step_decode_redo_impl(us_iter); - if (us_iter == us) { - break; - } - us_iter = (ImageUndoStep *)us_iter->step.next; - } + ImageUndoStep *us_iter = us; + while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { + if (us_iter->step.prev->is_applied == true) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.prev; + } + while (us_iter && (us_iter->step.is_applied == false)) { + image_undosys_step_decode_redo_impl(us_iter); + if (us_iter == us) { + break; + } + us_iter = (ImageUndoStep *)us_iter->step.next; + } } -static void image_undosys_step_decode(struct bContext *C, struct Main *bmain, UndoStep *us_p, int dir) +static void image_undosys_step_decode(struct bContext *C, + struct Main *bmain, + UndoStep *us_p, + int dir) { - ImageUndoStep *us = (ImageUndoStep *)us_p; + ImageUndoStep *us = (ImageUndoStep *)us_p; #if 0 - paint_undosys_step_decode_restore_ids(us); + paint_undosys_step_decode_restore_ids(us); #endif - if (dir < 0) { - image_undosys_step_decode_undo(us); - } - else { - image_undosys_step_decode_redo(us); - } + if (dir < 0) { + image_undosys_step_decode_undo(us); + } + else { + image_undosys_step_decode_redo(us); + } - if (us->paint_mode == PAINT_MODE_TEXTURE_3D) { - ED_object_mode_set(C, OB_MODE_TEXTURE_PAINT); - } + if (us->paint_mode == PAINT_MODE_TEXTURE_3D) { + ED_object_mode_set(C, OB_MODE_TEXTURE_PAINT); + } - /* Refresh texture slots. */ - ED_editors_init_for_undo(bmain); + /* Refresh texture slots. */ + ED_editors_init_for_undo(bmain); } static void image_undosys_step_free(UndoStep *us_p) { - ImageUndoStep *us = (ImageUndoStep *)us_p; - image_undo_free_list(&us->tiles); - BKE_undosys_ID_map_destroy(us->id_map); + ImageUndoStep *us = (ImageUndoStep *)us_p; + image_undo_free_list(&us->tiles); + BKE_undosys_ID_map_destroy(us->id_map); } -static void image_undosys_foreach_ID_ref( - UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) +static void image_undosys_foreach_ID_ref(UndoStep *us_p, + UndoTypeForEachIDRefFn foreach_ID_ref_fn, + void *user_data) { - ImageUndoStep *us = (ImageUndoStep *)us_p; - if (us->id_map != NULL) { - BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data); - } + ImageUndoStep *us = (ImageUndoStep *)us_p; + if (us->id_map != NULL) { + BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data); + } } /* Export for ED_undo_sys. */ void ED_image_undosys_type(UndoType *ut) { - ut->name = "Image"; - ut->poll = image_undosys_poll; - ut->step_encode_init = image_undosys_step_encode_init; - ut->step_encode = image_undosys_step_encode; - ut->step_decode = image_undosys_step_decode; - ut->step_free = image_undosys_step_free; + ut->name = "Image"; + ut->poll = image_undosys_poll; + ut->step_encode_init = image_undosys_step_encode_init; + ut->step_encode = image_undosys_step_encode; + ut->step_decode = image_undosys_step_decode; + ut->step_free = image_undosys_step_free; - ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref; + ut->step_foreach_ID_ref = image_undosys_foreach_ID_ref; - ut->use_context = true; + ut->use_context = true; - ut->step_size = sizeof(ImageUndoStep); + ut->step_size = sizeof(ImageUndoStep); } /** \} */ - /* -------------------------------------------------------------------- */ /** \name Utilities * \{ */ ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p) { - ImageUndoStep *us = (ImageUndoStep *)us_p; - return &us->tiles; + ImageUndoStep *us = (ImageUndoStep *)us_p; + return &us->tiles; } ListBase *ED_image_undo_get_tiles(void) { - UndoStack *ustack = ED_undo_stack_get(); - UndoStep *us_prev = ustack->step_init; - UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; - /* We should always have an undo push started when accessing tiles, - * not doing this means we won't have paint_mode correctly set. */ - BLI_assert(us_p == us_prev); - if (us_p != us_prev) { - /* Fallback value until we can be sure this never happens. */ - us->paint_mode = PAINT_MODE_TEXTURE_2D; - } - return ED_image_undosys_step_get_tiles(us_p); + UndoStack *ustack = ED_undo_stack_get(); + UndoStep *us_prev = ustack->step_init; + UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE); + ImageUndoStep *us = (ImageUndoStep *)us_p; + /* We should always have an undo push started when accessing tiles, + * not doing this means we won't have paint_mode correctly set. */ + BLI_assert(us_p == us_prev); + if (us_p != us_prev) { + /* Fallback value until we can be sure this never happens. */ + us->paint_mode = PAINT_MODE_TEXTURE_2D; + } + return ED_image_undosys_step_get_tiles(us_p); } /* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ void ED_image_undo_restore(UndoStep *us) { - ListBase *lb = ED_image_undosys_step_get_tiles(us); - image_undo_restore_runtime(lb); - image_undo_invalidate(); + ListBase *lb = ED_image_undosys_step_get_tiles(us); + image_undo_restore_runtime(lb); + image_undo_invalidate(); } void ED_image_undo_push_begin(const char *name, int paint_mode) { - UndoStack *ustack = ED_undo_stack_get(); - bContext *C = NULL; /* special case, we never read from this. */ - UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); - ImageUndoStep *us = (ImageUndoStep *)us_p; - BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); - us->paint_mode = paint_mode; + UndoStack *ustack = ED_undo_stack_get(); + bContext *C = NULL; /* special case, we never read from this. */ + UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE); + ImageUndoStep *us = (ImageUndoStep *)us_p; + BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D)); + us->paint_mode = paint_mode; } void ED_image_undo_push_end(void) { - UndoStack *ustack = ED_undo_stack_get(); - BKE_undosys_step_push(ustack, NULL, NULL); + UndoStack *ustack = ED_undo_stack_get(); + BKE_undosys_step_push(ustack, NULL, NULL); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 0e8262bd82f..1a8d2f79290 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -21,7 +21,6 @@ * \ingroup edsculpt */ - #ifndef __PAINT_INTERN_H__ #define __PAINT_INTERN_H__ @@ -51,22 +50,27 @@ struct wmWindowManager; enum ePaintMode; typedef struct CoNo { - float co[3]; - float no[3]; + float co[3]; + float no[3]; } CoNo; /* paint_stroke.c */ typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); -typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr); +typedef void (*StrokeUpdateStep)(struct bContext *C, + struct PaintStroke *stroke, + struct PointerRNA *itemptr); typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); -struct PaintStroke *paint_stroke_new( - struct bContext *C, struct wmOperator *op, - StrokeGetLocation get_location, StrokeTestStart test_start, - StrokeUpdateStep update_step, StrokeRedraw redraw, - StrokeDone done, int event_type); +struct PaintStroke *paint_stroke_new(struct bContext *C, + struct wmOperator *op, + StrokeGetLocation get_location, + StrokeTestStart test_start, + StrokeUpdateStep update_step, + StrokeRedraw redraw, + StrokeDone done, + int event_type); void paint_stroke_data_free(struct wmOperator *op); bool paint_space_stroke_enabled(struct Brush *br, enum ePaintMode mode); @@ -88,7 +92,9 @@ float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); bool paint_poll(struct bContext *C); void paint_cursor_start(struct bContext *C, bool (*poll)(struct bContext *C)); -void paint_cursor_start_explicit(struct Paint *p, struct wmWindowManager *wm, bool (*poll)(struct bContext *C)); +void paint_cursor_start_explicit(struct Paint *p, + struct wmWindowManager *wm, + bool (*poll)(struct bContext *C)); void paint_cursor_delete_textures(void); /* paint_vertex.c */ @@ -99,15 +105,17 @@ bool vertex_paint_poll(struct bContext *C); bool vertex_paint_poll_ignore_tool(struct bContext *C); bool vertex_paint_mode_poll(struct bContext *C); -typedef void (*VPaintTransform_Callback)(const float col[3], const void *user_data, float r_col[3]); +typedef void (*VPaintTransform_Callback)(const float col[3], + const void *user_data, + float r_col[3]); void PAINT_OT_weight_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_weight_paint(struct wmOperatorType *ot); void PAINT_OT_weight_set(struct wmOperatorType *ot); enum { - WPAINT_GRADIENT_TYPE_LINEAR, - WPAINT_GRADIENT_TYPE_RADIAL, + WPAINT_GRADIENT_TYPE_LINEAR, + WPAINT_GRADIENT_TYPE_RADIAL, }; void PAINT_OT_weight_gradient(struct wmOperatorType *ot); @@ -117,28 +125,31 @@ void PAINT_OT_vertex_paint(struct wmOperatorType *ot); unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp, bool secondary); /* paint_vertex_color_utils.c */ -unsigned int ED_vpaint_blend_tool( - const int tool, const uint col, - const uint paintcol, const int alpha_i); -bool ED_vpaint_color_transform( - struct Object *ob, VPaintTransform_Callback vpaint_tx_fn, const void *user_data); +unsigned int ED_vpaint_blend_tool(const int tool, + const uint col, + const uint paintcol, + const int alpha_i); +bool ED_vpaint_color_transform(struct Object *ob, + VPaintTransform_Callback vpaint_tx_fn, + const void *user_data); /* paint_vertex_weight_utils.c */ -float ED_wpaint_blend_tool( - const int tool, - const float weight, - const float paintval, const float alpha); +float ED_wpaint_blend_tool(const int tool, + const float weight, + const float paintval, + const float alpha); /* Utility for tools to ensure vertex groups exist before they begin. */ enum eWPaintFlag { - WPAINT_ENSURE_MIRROR = (1 << 0), + WPAINT_ENSURE_MIRROR = (1 << 0), }; struct WPaintVGroupIndex { - int active; - int mirror; + int active; + int mirror; }; -bool ED_wpaint_ensure_data( - struct bContext *C, struct ReportList *reports, - enum eWPaintFlag flag, struct WPaintVGroupIndex *vgroup_index); +bool ED_wpaint_ensure_data(struct bContext *C, + struct ReportList *reports, + enum eWPaintFlag flag, + struct WPaintVGroupIndex *vgroup_index); int ED_wpaint_mirror_vgroup_ensure(struct Object *ob, const int vgroup_active); /* paint_vertex_color_ops.c */ @@ -157,55 +168,77 @@ void PAINT_OT_weight_sample_group(struct wmOperatorType *ot); /* paint_vertex_proj.c */ struct VertProjHandle; -struct VertProjHandle *ED_vpaint_proj_handle_create( - struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, - struct CoNo **r_vcosnos); -void ED_vpaint_proj_handle_update( - struct Depsgraph *depsgraph, struct VertProjHandle *vp_handle, - /* runtime vars */ - struct ARegion *ar, const float mval_fl[2]); -void ED_vpaint_proj_handle_free( - struct VertProjHandle *vp_handle); - +struct VertProjHandle *ED_vpaint_proj_handle_create(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct CoNo **r_vcosnos); +void ED_vpaint_proj_handle_update(struct Depsgraph *depsgraph, + struct VertProjHandle *vp_handle, + /* runtime vars */ + struct ARegion *ar, + const float mval_fl[2]); +void ED_vpaint_proj_handle_free(struct VertProjHandle *vp_handle); /* paint_image.c */ typedef struct ImagePaintPartialRedraw { - int x1, y1, x2, y2; /* XXX, could use 'rcti' */ - int enabled; + int x1, y1, x2, y2; /* XXX, could use 'rcti' */ + int enabled; } ImagePaintPartialRedraw; -#define IMAPAINT_TILE_BITS 6 -#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) -#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) +#define IMAPAINT_TILE_BITS 6 +#define IMAPAINT_TILE_SIZE (1 << IMAPAINT_TILE_BITS) +#define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) bool image_texture_paint_poll(struct bContext *C); -void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint); +void imapaint_image_update(struct SpaceImage *sima, + struct Image *image, + struct ImBuf *ibuf, + short texpaint); struct ImagePaintPartialRedraw *get_imapaintpartial(void); void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr); -void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th); +void imapaint_region_tiles( + struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th); int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy); void *paint_2d_new_stroke(struct bContext *, struct wmOperator *, int mode); void paint_2d_redraw(const struct bContext *C, void *ps, bool final); void paint_2d_stroke_done(void *ps); -void paint_2d_stroke( - void *ps, const float prev_mval[2], const float mval[2], - const bool eraser, float pressure, float distance, float size); -void paint_2d_bucket_fill( - const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps); -void paint_2d_gradient_fill( - const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); -void *paint_proj_new_stroke( - struct bContext *C, struct Object *ob, const float mouse[2], int mode); -void paint_proj_stroke( - const struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2], - const bool eraser, float pressure, float distance, float size); +void paint_2d_stroke(void *ps, + const float prev_mval[2], + const float mval[2], + const bool eraser, + float pressure, + float distance, + float size); +void paint_2d_bucket_fill(const struct bContext *C, + const float color[3], + struct Brush *br, + const float mouse_init[2], + void *ps); +void paint_2d_gradient_fill(const struct bContext *C, + struct Brush *br, + const float mouse_init[2], + const float mouse_final[2], + void *ps); +void *paint_proj_new_stroke(struct bContext *C, struct Object *ob, const float mouse[2], int mode); +void paint_proj_stroke(const struct bContext *C, + void *ps, + const float prevmval_i[2], + const float mval_i[2], + const bool eraser, + float pressure, + float distance, + float size); void paint_proj_redraw(const struct bContext *C, void *pps, bool final); void paint_proj_stroke_done(void *ps); -void paint_brush_color_get( - struct Scene *scene, struct Brush *br, - bool color_correction, bool invert, float distance, float pressure, float color[3], - struct ColorManagedDisplay *display); +void paint_brush_color_get(struct Scene *scene, + struct Brush *br, + bool color_correction, + bool invert, + float distance, + float pressure, + float color[3], + struct ColorManagedDisplay *display); bool paint_use_opacity_masking(struct Brush *brush); void paint_brush_init_tex(struct Brush *brush); void paint_brush_exit_tex(struct Brush *brush); @@ -221,14 +254,23 @@ void PAINT_OT_image_paint(struct wmOperatorType *ot); void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot); /* paint_image_undo.c */ -void *image_undo_find_tile( - ListBase *undo_tiles, - struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, - unsigned short **mask, bool validate); -void *image_undo_push_tile( - ListBase *undo_tiles, - struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, - unsigned short **, bool **valid, bool proj, bool find_prev); +void *image_undo_find_tile(ListBase *undo_tiles, + struct Image *ima, + struct ImBuf *ibuf, + int x_tile, + int y_tile, + unsigned short **mask, + bool validate); +void *image_undo_push_tile(ListBase *undo_tiles, + struct Image *ima, + struct ImBuf *ibuf, + struct ImBuf **tmpibuf, + int x_tile, + int y_tile, + unsigned short **, + bool **valid, + bool proj, + bool find_prev); void image_undo_remove_masks(void); void image_undo_init_locks(void); void image_undo_end_locks(void); @@ -247,30 +289,37 @@ void SCULPT_OT_uv_sculpt_stroke(struct wmOperatorType *ot); /* Convert the object-space axis-aligned bounding box (expressed as * its minimum and maximum corners) into a screen-space rectangle, * returns zero if the result is empty */ -bool paint_convert_bb_to_rect( - struct rcti *rect, - const float bb_min[3], - const float bb_max[3], - const struct ARegion *ar, - struct RegionView3D *rv3d, - struct Object *ob); +bool paint_convert_bb_to_rect(struct rcti *rect, + const float bb_min[3], + const float bb_max[3], + const struct ARegion *ar, + struct RegionView3D *rv3d, + struct Object *ob); /* Get four planes in object-space that describe the projection of * screen_rect from screen into object-space (essentially converting a * 2D screens-space bounding box into four 3D planes) */ -void paint_calc_redraw_planes( - float planes[4][4], - const struct ARegion *ar, - struct Object *ob, - const struct rcti *screen_rect); - -float paint_calc_object_space_radius(struct ViewContext *vc, const float center[3], float pixel_radius); -float paint_get_tex_pixel(const struct MTex *mtex, float u, float v, struct ImagePool *pool, int thread); -void paint_get_tex_pixel_col( - const struct MTex *mtex, float u, float v, float rgba[4], - struct ImagePool *pool, int thread, bool convert, struct ColorSpace *colorspace); - -void paint_sample_color(struct bContext *C, struct ARegion *ar, int x, int y, bool texpaint_proj, bool palette); +void paint_calc_redraw_planes(float planes[4][4], + const struct ARegion *ar, + struct Object *ob, + const struct rcti *screen_rect); + +float paint_calc_object_space_radius(struct ViewContext *vc, + const float center[3], + float pixel_radius); +float paint_get_tex_pixel( + const struct MTex *mtex, float u, float v, struct ImagePool *pool, int thread); +void paint_get_tex_pixel_col(const struct MTex *mtex, + float u, + float v, + float rgba[4], + struct ImagePool *pool, + int thread, + bool convert, + struct ColorSpace *colorspace); + +void paint_sample_color( + struct bContext *C, struct ARegion *ar, int x, int y, bool texpaint_proj, bool palette); void paint_stroke_operator_properties(struct wmOperatorType *ot); @@ -295,23 +344,23 @@ void flip_qt_qt(float out[3], const float in[3], const char symm); /* stroke operator */ typedef enum BrushStrokeMode { - BRUSH_STROKE_NORMAL, - BRUSH_STROKE_INVERT, - BRUSH_STROKE_SMOOTH, + BRUSH_STROKE_NORMAL, + BRUSH_STROKE_INVERT, + BRUSH_STROKE_SMOOTH, } BrushStrokeMode; /* paint_hide.c */ typedef enum { - PARTIALVIS_HIDE, - PARTIALVIS_SHOW, + PARTIALVIS_HIDE, + PARTIALVIS_SHOW, } PartialVisAction; typedef enum { - PARTIALVIS_INSIDE, - PARTIALVIS_OUTSIDE, - PARTIALVIS_ALL, - PARTIALVIS_MASKED, + PARTIALVIS_INSIDE, + PARTIALVIS_OUTSIDE, + PARTIALVIS_ALL, + PARTIALVIS_MASKED, } PartialVisArea; void PAINT_OT_hide_show(struct wmOperatorType *ot); @@ -319,9 +368,9 @@ void PAINT_OT_hide_show(struct wmOperatorType *ot); /* paint_mask.c */ typedef enum { - PAINT_MASK_FLOOD_VALUE, - PAINT_MASK_FLOOD_VALUE_INVERSE, - PAINT_MASK_INVERT, + PAINT_MASK_FLOOD_VALUE, + PAINT_MASK_FLOOD_VALUE_INVERSE, + PAINT_MASK_INVERT, } PaintMaskFloodMode; void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot); @@ -338,10 +387,10 @@ void PAINTCURVE_OT_cursor(struct wmOperatorType *ot); /* image painting blur kernel */ typedef struct { - float *wdata; /* actual kernel */ - int side; /* kernel side */ - int side_squared; /* data side */ - int pixel_len; /* pixels around center that kernel is wide */ + float *wdata; /* actual kernel */ + int side; /* kernel side */ + int side_squared; /* data side */ + int pixel_len; /* pixels around center that kernel is wide */ } BlurKernel; enum eBlurKernelType; diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b88ae861229..ca48030daed 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -60,478 +60,503 @@ #include <stdlib.h> static const EnumPropertyItem mode_items[] = { - {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"}, - {PAINT_MASK_FLOOD_VALUE_INVERSE, "VALUE_INVERSE", 0, "Value Inverted", "Set mask to the level specified by the inverted 'value' property"}, - {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"}, - {0}}; - - -static void mask_flood_fill_set_elem( - float *elem, - PaintMaskFloodMode mode, - float value) + {PAINT_MASK_FLOOD_VALUE, + "VALUE", + 0, + "Value", + "Set mask to the level specified by the 'value' property"}, + {PAINT_MASK_FLOOD_VALUE_INVERSE, + "VALUE_INVERSE", + 0, + "Value Inverted", + "Set mask to the level specified by the inverted 'value' property"}, + {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"}, + {0}}; + +static void mask_flood_fill_set_elem(float *elem, PaintMaskFloodMode mode, float value) { - switch (mode) { - case PAINT_MASK_FLOOD_VALUE: - (*elem) = value; - break; - case PAINT_MASK_FLOOD_VALUE_INVERSE: - (*elem) = 1.0f - value; - break; - case PAINT_MASK_INVERT: - (*elem) = 1.0f - (*elem); - break; - } + switch (mode) { + case PAINT_MASK_FLOOD_VALUE: + (*elem) = value; + break; + case PAINT_MASK_FLOOD_VALUE_INVERSE: + (*elem) = 1.0f - value; + break; + case PAINT_MASK_INVERT: + (*elem) = 1.0f - (*elem); + break; + } } typedef struct MaskTaskData { - Object *ob; - PBVH *pbvh; - PBVHNode **nodes; - bool multires; - - PaintMaskFloodMode mode; - float value; - float (*clip_planes_final)[4]; + Object *ob; + PBVH *pbvh; + PBVHNode **nodes; + bool multires; + + PaintMaskFloodMode mode; + float value; + float (*clip_planes_final)[4]; } MaskTaskData; -static void mask_flood_fill_task_cb( - void *__restrict userdata, - const int i, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void mask_flood_fill_task_cb(void *__restrict userdata, + const int i, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - MaskTaskData *data = userdata; + MaskTaskData *data = userdata; - PBVHNode *node = data->nodes[i]; + PBVHNode *node = data->nodes[i]; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; + const PaintMaskFloodMode mode = data->mode; + const float value = data->value; - PBVHVertexIter vi; + PBVHVertexIter vi; - sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { - mask_flood_fill_set_elem(vi.mask, mode, value); - } BKE_pbvh_vertex_iter_end; + BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) + { + mask_flood_fill_set_elem(vi.mask, mode, value); + } + BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_redraw(node); - if (data->multires) - BKE_pbvh_node_mark_normals_update(node); + BKE_pbvh_node_mark_redraw(node); + if (data->multires) + BKE_pbvh_node_mark_normals_update(node); } static int mask_flood_fill_exec(bContext *C, wmOperator *op) { - ARegion *ar = CTX_wm_region(C); - struct Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - PaintMaskFloodMode mode; - float value; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - bool multires; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ARegion *ar = CTX_wm_region(C); + struct Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + PaintMaskFloodMode mode; + float value; + PBVH *pbvh; + PBVHNode **nodes; + int totnode; + bool multires; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - mode = RNA_enum_get(op->ptr, "mode"); - value = RNA_float_get(op->ptr, "value"); + mode = RNA_enum_get(op->ptr, "mode"); + value = RNA_float_get(op->ptr, "value"); - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, true); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, true); + pbvh = ob->sculpt->pbvh; + multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); - sculpt_undo_push_begin("Mask flood fill"); + sculpt_undo_push_begin("Mask flood fill"); - MaskTaskData data = { - .ob = ob, .pbvh = pbvh, .nodes = nodes, .multires = multires, - .mode = mode, .value = value, - }; + MaskTaskData data = { + .ob = ob, + .pbvh = pbvh, + .nodes = nodes, + .multires = multires, + .mode = mode, + .value = value, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range( - 0, totnode, &data, mask_flood_fill_task_cb, - &settings); + 0, totnode, &data, mask_flood_fill_task_cb, &settings); - if (multires) - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + if (multires) + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); - sculpt_undo_push_end(); + sculpt_undo_push_end(); - if (nodes) - MEM_freeN(nodes); + if (nodes) + MEM_freeN(nodes); - ED_region_tag_redraw(ar); + ED_region_tag_redraw(ar); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) { - /* identifiers */ - ot->name = "Mask Flood Fill"; - ot->idname = "PAINT_OT_mask_flood_fill"; - ot->description = "Fill the whole mask with a given value, or invert its values"; - - /* api callbacks */ - ot->exec = mask_flood_fill_exec; - ot->poll = sculpt_mode_poll; - - ot->flag = OPTYPE_REGISTER; - - /* rna */ - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float(ot->srna, "value", 0, 0, 1, "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1); + /* identifiers */ + ot->name = "Mask Flood Fill"; + ot->idname = "PAINT_OT_mask_flood_fill"; + ot->description = "Fill the whole mask with a given value, or invert its values"; + + /* api callbacks */ + ot->exec = mask_flood_fill_exec; + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* rna */ + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float( + ot->srna, + "value", + 0, + 0, + 1, + "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", + 0, + 1); } /* Box select, operator is VIEW3D_OT_select_box, defined in view3d_select.c */ static bool is_effected(float planes[4][4], const float co[3]) { - return isect_point_planes_v3(planes, 4, co); + return isect_point_planes_v3(planes, 4, co); } static void flip_plane(float out[4], const float in[4], const char symm) { - if (symm & PAINT_SYMM_X) - out[0] = -in[0]; - else - out[0] = in[0]; - if (symm & PAINT_SYMM_Y) - out[1] = -in[1]; - else - out[1] = in[1]; - if (symm & PAINT_SYMM_Z) - out[2] = -in[2]; - else - out[2] = in[2]; - - out[3] = in[3]; + if (symm & PAINT_SYMM_X) + out[0] = -in[0]; + else + out[0] = in[0]; + if (symm & PAINT_SYMM_Y) + out[1] = -in[1]; + else + out[1] = in[1]; + if (symm & PAINT_SYMM_Z) + out[2] = -in[2]; + else + out[2] = in[2]; + + out[3] = in[3]; } -static void mask_box_select_task_cb( - void *__restrict userdata, - const int i, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void mask_box_select_task_cb(void *__restrict userdata, + const int i, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - MaskTaskData *data = userdata; + MaskTaskData *data = userdata; - PBVHNode *node = data->nodes[i]; + PBVHNode *node = data->nodes[i]; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; - float (*clip_planes_final)[4] = data->clip_planes_final; + const PaintMaskFloodMode mode = data->mode; + const float value = data->value; + float(*clip_planes_final)[4] = data->clip_planes_final; - PBVHVertexIter vi; - bool any_masked = false; + PBVHVertexIter vi; + bool any_masked = false; - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { - if (is_effected(clip_planes_final, vi.co)) { - if (!any_masked) { - any_masked = true; + BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) + { + if (is_effected(clip_planes_final, vi.co)) { + if (!any_masked) { + any_masked = true; - sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); - if (data->multires) - BKE_pbvh_node_mark_normals_update(node); - } - mask_flood_fill_set_elem(vi.mask, mode, value); - } - } BKE_pbvh_vertex_iter_end; + BKE_pbvh_node_mark_redraw(node); + if (data->multires) + BKE_pbvh_node_mark_normals_update(node); + } + mask_flood_fill_set_elem(vi.mask, mode, value); + } + } + BKE_pbvh_vertex_iter_end; } bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Sculpt *sd = vc->scene->toolsettings->sculpt; - BoundBox bb; - float clip_planes[4][4]; - float clip_planes_final[4][4]; - ARegion *ar = vc->ar; - struct Scene *scene = vc->scene; - Object *ob = vc->obact; - PaintMaskFloodMode mode; - float value; - bool multires; - PBVH *pbvh; - PBVHNode **nodes; - int totnode, symmpass; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - - mode = PAINT_MASK_FLOOD_VALUE; - value = select ? 1.0 : 0.0; - - /* transform the clip planes in object space */ - ED_view3d_clipping_calc(&bb, clip_planes, vc->ar, vc->obact, rect); - negate_m4(clip_planes); - - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, true); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); - - sculpt_undo_push_begin("Mask box fill"); - - for (symmpass = 0; symmpass <= symm; ++symmpass) { - if (symmpass == 0 || - (symm & symmpass && - (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) - { - int j = 0; - - /* flip the planes symmetrically as needed */ - for (; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } - - BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); - - MaskTaskData data = { - .ob = ob, .pbvh = pbvh, .nodes = nodes, .multires = multires, - .mode = mode, .value = value, .clip_planes_final = clip_planes_final, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, &data, mask_box_select_task_cb, - &settings); - - if (nodes) - MEM_freeN(nodes); - } - } - - if (multires) - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); - - sculpt_undo_push_end(); - - ED_region_tag_redraw(ar); - - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return true; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Sculpt *sd = vc->scene->toolsettings->sculpt; + BoundBox bb; + float clip_planes[4][4]; + float clip_planes_final[4][4]; + ARegion *ar = vc->ar; + struct Scene *scene = vc->scene; + Object *ob = vc->obact; + PaintMaskFloodMode mode; + float value; + bool multires; + PBVH *pbvh; + PBVHNode **nodes; + int totnode, symmpass; + int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + + mode = PAINT_MASK_FLOOD_VALUE; + value = select ? 1.0 : 0.0; + + /* transform the clip planes in object space */ + ED_view3d_clipping_calc(&bb, clip_planes, vc->ar, vc->obact, rect); + negate_m4(clip_planes); + + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, true); + pbvh = ob->sculpt->pbvh; + multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); + + sculpt_undo_push_begin("Mask box fill"); + + for (symmpass = 0; symmpass <= symm; ++symmpass) { + if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) && + (symm != 6 || (symmpass != 3 && symmpass != 5)))) { + int j = 0; + + /* flip the planes symmetrically as needed */ + for (; j < 4; j++) { + flip_plane(clip_planes_final[j], clip_planes[j], symmpass); + } + + BKE_pbvh_search_gather( + pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); + + MaskTaskData data = { + .ob = ob, + .pbvh = pbvh, + .nodes = nodes, + .multires = multires, + .mode = mode, + .value = value, + .clip_planes_final = clip_planes_final, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && + totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); + + if (nodes) + MEM_freeN(nodes); + } + } + + if (multires) + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + + sculpt_undo_push_end(); + + ED_region_tag_redraw(ar); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return true; } typedef struct LassoMaskData { - struct ViewContext *vc; - float projviewobjmat[4][4]; - BLI_bitmap *px; - int width; - rcti rect; /* bounding box for scanfilling */ - int symmpass; - - MaskTaskData task_data; + struct ViewContext *vc; + float projviewobjmat[4][4]; + BLI_bitmap *px; + int width; + rcti rect; /* bounding box for scanfilling */ + int symmpass; + + MaskTaskData task_data; } LassoMaskData; - /* Lasso select. This could be defined as part of VIEW3D_OT_select_lasso, still the shortcuts conflict, * so we will use a separate operator */ static bool is_effected_lasso(LassoMaskData *data, float co[3]) { - float scr_co_f[2]; - int scr_co_s[2]; - float co_final[3]; - - flip_v3_v3(co_final, co, data->symmpass); - /* first project point to 2d space */ - ED_view3d_project_float_v2_m4(data->vc->ar, co_final, scr_co_f, data->projviewobjmat); - - scr_co_s[0] = scr_co_f[0]; - scr_co_s[1] = scr_co_f[1]; - - /* clip against screen, because lasso is limited to screen only */ - if ((scr_co_s[0] < data->rect.xmin) || - (scr_co_s[1] < data->rect.ymin) || - (scr_co_s[0] >= data->rect.xmax) || - (scr_co_s[1] >= data->rect.ymax)) - { - return false; - } - - scr_co_s[0] -= data->rect.xmin; - scr_co_s[1] -= data->rect.ymin; - - return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]); + float scr_co_f[2]; + int scr_co_s[2]; + float co_final[3]; + + flip_v3_v3(co_final, co, data->symmpass); + /* first project point to 2d space */ + ED_view3d_project_float_v2_m4(data->vc->ar, co_final, scr_co_f, data->projviewobjmat); + + scr_co_s[0] = scr_co_f[0]; + scr_co_s[1] = scr_co_f[1]; + + /* clip against screen, because lasso is limited to screen only */ + if ((scr_co_s[0] < data->rect.xmin) || (scr_co_s[1] < data->rect.ymin) || + (scr_co_s[0] >= data->rect.xmax) || (scr_co_s[1] >= data->rect.ymax)) { + return false; + } + + scr_co_s[0] -= data->rect.xmin; + scr_co_s[1] -= data->rect.ymin; + + return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]); } static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data) { - LassoMaskData *data = user_data; - int index = (y * data->width) + x; - int index_end = (y * data->width) + x_end; - do { - BLI_BITMAP_ENABLE(data->px, index); - } while (++index != index_end); + LassoMaskData *data = user_data; + int index = (y * data->width) + x; + int index_end = (y * data->width) + x_end; + do { + BLI_BITMAP_ENABLE(data->px, index); + } while (++index != index_end); } -static void mask_gesture_lasso_task_cb( - void *__restrict userdata, - const int i, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void mask_gesture_lasso_task_cb(void *__restrict userdata, + const int i, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - LassoMaskData *lasso_data = userdata; - MaskTaskData *data = &lasso_data->task_data; + LassoMaskData *lasso_data = userdata; + MaskTaskData *data = &lasso_data->task_data; - PBVHNode *node = data->nodes[i]; + PBVHNode *node = data->nodes[i]; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; + const PaintMaskFloodMode mode = data->mode; + const float value = data->value; - PBVHVertexIter vi; - bool any_masked = false; + PBVHVertexIter vi; + bool any_masked = false; - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) { - if (is_effected_lasso(lasso_data, vi.co)) { - if (!any_masked) { - any_masked = true; + BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) + { + if (is_effected_lasso(lasso_data, vi.co)) { + if (!any_masked) { + any_masked = true; - sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); - if (data->multires) - BKE_pbvh_node_mark_normals_update(node); - } + BKE_pbvh_node_mark_redraw(node); + if (data->multires) + BKE_pbvh_node_mark_normals_update(node); + } - mask_flood_fill_set_elem(vi.mask, mode, value); - } - } BKE_pbvh_vertex_iter_end; + mask_flood_fill_set_elem(vi.mask, mode, value); + } + } + BKE_pbvh_vertex_iter_end; } static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) { - int mcords_tot; - const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); - - if (mcords) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - float clip_planes[4][4], clip_planes_final[4][4]; - BoundBox bb; - Object *ob; - ViewContext vc; - LassoMaskData data; - struct Scene *scene = CTX_data_scene(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - PBVH *pbvh; - PBVHNode **nodes; - int totnode, symmpass; - bool multires; - PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - float value = RNA_float_get(op->ptr, "value"); - - /* Calculations of individual vertices are done in 2D screen space to diminish the amount of - * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle - * of lasso */ - ED_view3d_viewcontext_init(C, &vc); - - /* lasso data calculations */ - data.vc = &vc; - ob = vc.obact; - ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); - - BLI_lasso_boundbox(&data.rect, mcords, mcords_tot); - data.width = data.rect.xmax - data.rect.xmin; - data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); - - BLI_bitmap_draw_2d_poly_v2i_n( - data.rect.xmin, data.rect.ymin, data.rect.xmax, data.rect.ymax, - mcords, mcords_tot, - mask_lasso_px_cb, &data); - - ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, &data.rect); - negate_m4(clip_planes); - - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, true); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); - - sculpt_undo_push_begin("Mask lasso fill"); - - for (symmpass = 0; symmpass <= symm; ++symmpass) { - if ((symmpass == 0) || - (symm & symmpass && - (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) - { - int j = 0; - - /* flip the planes symmetrically as needed */ - for (; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } - - data.symmpass = symmpass; - - /* gather nodes inside lasso's enclosing rectangle - * (should greatly help with bigger meshes) */ - BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); - - data.task_data.ob = ob; - data.task_data.pbvh = pbvh; - data.task_data.nodes = nodes; - data.task_data.multires = multires; - data.task_data.mode = mode; - data.task_data.value = value; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && (totnode > SCULPT_THREADED_LIMIT)); - BLI_task_parallel_range( - 0, totnode, &data, mask_gesture_lasso_task_cb, - &settings); - - if (nodes) - MEM_freeN(nodes); - } - } - - if (multires) - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); - - sculpt_undo_push_end(); - - ED_region_tag_redraw(vc.ar); - MEM_freeN((void *)mcords); - MEM_freeN(data.px); - - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; - } - return OPERATOR_PASS_THROUGH; + int mcords_tot; + const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + + if (mcords) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); + float clip_planes[4][4], clip_planes_final[4][4]; + BoundBox bb; + Object *ob; + ViewContext vc; + LassoMaskData data; + struct Scene *scene = CTX_data_scene(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + PBVH *pbvh; + PBVHNode **nodes; + int totnode, symmpass; + bool multires; + PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); + float value = RNA_float_get(op->ptr, "value"); + + /* Calculations of individual vertices are done in 2D screen space to diminish the amount of + * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle + * of lasso */ + ED_view3d_viewcontext_init(C, &vc); + + /* lasso data calculations */ + data.vc = &vc; + ob = vc.obact; + ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); + + BLI_lasso_boundbox(&data.rect, mcords, mcords_tot); + data.width = data.rect.xmax - data.rect.xmin; + data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); + + BLI_bitmap_draw_2d_poly_v2i_n(data.rect.xmin, + data.rect.ymin, + data.rect.xmax, + data.rect.ymax, + mcords, + mcords_tot, + mask_lasso_px_cb, + &data); + + ED_view3d_clipping_calc(&bb, clip_planes, vc.ar, vc.obact, &data.rect); + negate_m4(clip_planes); + + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, true); + pbvh = ob->sculpt->pbvh; + multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); + + sculpt_undo_push_begin("Mask lasso fill"); + + for (symmpass = 0; symmpass <= symm; ++symmpass) { + if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) && + (symm != 6 || (symmpass != 3 && symmpass != 5)))) { + int j = 0; + + /* flip the planes symmetrically as needed */ + for (; j < 4; j++) { + flip_plane(clip_planes_final[j], clip_planes[j], symmpass); + } + + data.symmpass = symmpass; + + /* gather nodes inside lasso's enclosing rectangle + * (should greatly help with bigger meshes) */ + BKE_pbvh_search_gather( + pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); + + data.task_data.ob = ob; + data.task_data.pbvh = pbvh; + data.task_data.nodes = nodes; + data.task_data.multires = multires; + data.task_data.mode = mode; + data.task_data.value = value; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && + (totnode > SCULPT_THREADED_LIMIT)); + BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); + + if (nodes) + MEM_freeN(nodes); + } + } + + if (multires) + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + + sculpt_undo_push_end(); + + ED_region_tag_redraw(vc.ar); + MEM_freeN((void *)mcords); + MEM_freeN(data.px); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; + } + return OPERATOR_PASS_THROUGH; } void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) { - ot->name = "Mask Lasso Gesture"; - ot->idname = "PAINT_OT_mask_lasso_gesture"; - ot->description = "Add mask within the lasso as you move the brush"; - - ot->invoke = WM_gesture_lasso_invoke; - ot->modal = WM_gesture_lasso_modal; - ot->exec = paint_mask_gesture_lasso_exec; - - ot->poll = sculpt_mode_poll; - - ot->flag = OPTYPE_REGISTER; - - /* properties */ - WM_operator_properties_gesture_lasso(ot); - - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float(ot->srna, "value", 1.0, 0, 1.0, "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1); + ot->name = "Mask Lasso Gesture"; + ot->idname = "PAINT_OT_mask_lasso_gesture"; + ot->description = "Add mask within the lasso as you move the brush"; + + ot->invoke = WM_gesture_lasso_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = paint_mask_gesture_lasso_exec; + + ot->poll = sculpt_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* properties */ + WM_operator_properties_gesture_lasso(ot); + + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float( + ot->srna, + "value", + 1.0, + 0, + 1.0, + "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", + 0, + 1); } diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 218d3ce4273..6686e255b08 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -66,1030 +66,1027 @@ /* Brush operators */ static int brush_add_exec(bContext *C, wmOperator *UNUSED(op)) { - /*int type = RNA_enum_get(op->ptr, "type");*/ - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); - Main *bmain = CTX_data_main(C); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - - if (br) { - br = BKE_brush_copy(bmain, br); - } - else { - br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paintmode(mode)); - id_us_min(&br->id); /* fake user only */ - } - - BKE_paint_brush_set(paint, br); - - return OPERATOR_FINISHED; + /*int type = RNA_enum_get(op->ptr, "type");*/ + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *br = BKE_paint_brush(paint); + Main *bmain = CTX_data_main(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + + if (br) { + br = BKE_brush_copy(bmain, br); + } + else { + br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paintmode(mode)); + id_us_min(&br->id); /* fake user only */ + } + + BKE_paint_brush_set(paint, br); + + return OPERATOR_FINISHED; } static void BRUSH_OT_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add Brush"; - ot->description = "Add brush by mode type"; - ot->idname = "BRUSH_OT_add"; + /* identifiers */ + ot->name = "Add Brush"; + ot->description = "Add brush by mode type"; + ot->idname = "BRUSH_OT_add"; - /* api callbacks */ - ot->exec = brush_add_exec; + /* api callbacks */ + ot->exec = brush_add_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) { - /*int type = RNA_enum_get(op->ptr, "type");*/ - ToolSettings *ts = CTX_data_tool_settings(C); - Paint *paint = &ts->gp_paint->paint; - Brush *br = BKE_paint_brush(paint); - Main *bmain = CTX_data_main(C); - // ePaintMode mode = PAINT_MODE_GPENCIL; - - if (br) { - br = BKE_brush_copy(bmain, br); - } - else { - br = BKE_brush_add(bmain, "Brush", OB_MODE_PAINT_GPENCIL); - id_us_min(&br->id); /* fake user only */ - } - - BKE_paint_brush_set(paint, br); - - /* init grease pencil specific data */ - BKE_brush_init_gpencil_settings(br); - - return OPERATOR_FINISHED; + /*int type = RNA_enum_get(op->ptr, "type");*/ + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = &ts->gp_paint->paint; + Brush *br = BKE_paint_brush(paint); + Main *bmain = CTX_data_main(C); + // ePaintMode mode = PAINT_MODE_GPENCIL; + + if (br) { + br = BKE_brush_copy(bmain, br); + } + else { + br = BKE_brush_add(bmain, "Brush", OB_MODE_PAINT_GPENCIL); + id_us_min(&br->id); /* fake user only */ + } + + BKE_paint_brush_set(paint, br); + + /* init grease pencil specific data */ + BKE_brush_init_gpencil_settings(br); + + return OPERATOR_FINISHED; } static void BRUSH_OT_add_gpencil(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add Drawing Brush"; - ot->description = "Add brush for Grease Pencil"; - ot->idname = "BRUSH_OT_add_gpencil"; + /* identifiers */ + ot->name = "Add Drawing Brush"; + ot->description = "Add brush for Grease Pencil"; + ot->idname = "BRUSH_OT_add_gpencil"; - /* api callbacks */ - ot->exec = brush_add_gpencil_exec; + /* api callbacks */ + ot->exec = brush_add_gpencil_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int brush_scale_size_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - // Object *ob = CTX_data_active_object(C); - float scalar = RNA_float_get(op->ptr, "scalar"); - - if (brush) { - // pixel radius - { - const int old_size = BKE_brush_size_get(scene, brush); - int size = (int)(scalar * old_size); - - if (abs(old_size - size) < U.pixelsize) { - if (scalar > 1) { - size += U.pixelsize; - } - else if (scalar < 1) { - size -= U.pixelsize; - } - } - - BKE_brush_size_set(scene, brush, size); - } - - // unprojected radius - { - float unprojected_radius = scalar * BKE_brush_unprojected_radius_get(scene, brush); - - if (unprojected_radius < 0.001f) // XXX magic number - unprojected_radius = 0.001f; - - BKE_brush_unprojected_radius_set(scene, brush, unprojected_radius); - } - - WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); - } - - return OPERATOR_FINISHED; + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + // Object *ob = CTX_data_active_object(C); + float scalar = RNA_float_get(op->ptr, "scalar"); + + if (brush) { + // pixel radius + { + const int old_size = BKE_brush_size_get(scene, brush); + int size = (int)(scalar * old_size); + + if (abs(old_size - size) < U.pixelsize) { + if (scalar > 1) { + size += U.pixelsize; + } + else if (scalar < 1) { + size -= U.pixelsize; + } + } + + BKE_brush_size_set(scene, brush, size); + } + + // unprojected radius + { + float unprojected_radius = scalar * BKE_brush_unprojected_radius_get(scene, brush); + + if (unprojected_radius < 0.001f) // XXX magic number + unprojected_radius = 0.001f; + + BKE_brush_unprojected_radius_set(scene, brush, unprojected_radius); + } + + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + } + + return OPERATOR_FINISHED; } static void BRUSH_OT_scale_size(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Scale Sculpt/Paint Brush Size"; - ot->description = "Change brush size by a scalar"; - ot->idname = "BRUSH_OT_scale_size"; + /* identifiers */ + ot->name = "Scale Sculpt/Paint Brush Size"; + ot->description = "Change brush size by a scalar"; + ot->idname = "BRUSH_OT_scale_size"; - /* api callbacks */ - ot->exec = brush_scale_size_exec; + /* api callbacks */ + ot->exec = brush_scale_size_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2); + RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2); } /* Palette operators */ static int palette_new_exec(bContext *C, wmOperator *UNUSED(op)) { - Paint *paint = BKE_paint_get_active_from_context(C); - Main *bmain = CTX_data_main(C); - Palette *palette; + Paint *paint = BKE_paint_get_active_from_context(C); + Main *bmain = CTX_data_main(C); + Palette *palette; - palette = BKE_palette_add(bmain, "Palette"); + palette = BKE_palette_add(bmain, "Palette"); - BKE_paint_palette_set(paint, palette); + BKE_paint_palette_set(paint, palette); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void PALETTE_OT_new(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Add New Palette"; - ot->description = "Add new palette"; - ot->idname = "PALETTE_OT_new"; + /* identifiers */ + ot->name = "Add New Palette"; + ot->description = "Add new palette"; + ot->idname = "PALETTE_OT_new"; - /* api callbacks */ - ot->exec = palette_new_exec; + /* api callbacks */ + ot->exec = palette_new_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static bool palette_poll(bContext *C) { - Paint *paint = BKE_paint_get_active_from_context(C); + Paint *paint = BKE_paint_get_active_from_context(C); - if (paint && paint->palette != NULL) - return true; + if (paint && paint->palette != NULL) + return true; - return false; + return false; } static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = paint->brush; - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - Palette *palette = paint->palette; - PaletteColor *color; - - color = BKE_palette_color_add(palette); - palette->active_color = BLI_listbase_count(&palette->colors) - 1; - - if (ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX)) { - copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); - color->value = 0.0; - } - else if (mode == PAINT_MODE_WEIGHT) { - zero_v3(color->rgb); - color->value = brush->weight; - } - - return OPERATOR_FINISHED; + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = paint->brush; + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + Palette *palette = paint->palette; + PaletteColor *color; + + color = BKE_palette_color_add(palette); + palette->active_color = BLI_listbase_count(&palette->colors) - 1; + + if (ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX)) { + copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); + color->value = 0.0; + } + else if (mode == PAINT_MODE_WEIGHT) { + zero_v3(color->rgb); + color->value = brush->weight; + } + + return OPERATOR_FINISHED; } static void PALETTE_OT_color_add(wmOperatorType *ot) { - /* identifiers */ - ot->name = "New Palette Color"; - ot->description = "Add new color to active palette"; - ot->idname = "PALETTE_OT_color_add"; - - /* api callbacks */ - ot->exec = palette_color_add_exec; - ot->poll = palette_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "New Palette Color"; + ot->description = "Add new color to active palette"; + ot->idname = "PALETTE_OT_color_add"; + + /* api callbacks */ + ot->exec = palette_color_add_exec; + ot->poll = palette_poll; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int palette_color_delete_exec(bContext *C, wmOperator *UNUSED(op)) { - Paint *paint = BKE_paint_get_active_from_context(C); - Palette *palette = paint->palette; - PaletteColor *color = BLI_findlink(&palette->colors, palette->active_color); + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + PaletteColor *color = BLI_findlink(&palette->colors, palette->active_color); - if (color) { - BKE_palette_color_remove(palette, color); - } + if (color) { + BKE_palette_color_remove(palette, color); + } - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void PALETTE_OT_color_delete(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Delete Palette Color"; - ot->description = "Remove active color from palette"; - ot->idname = "PALETTE_OT_color_delete"; - - /* api callbacks */ - ot->exec = palette_color_delete_exec; - ot->poll = palette_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Delete Palette Color"; + ot->description = "Remove active color from palette"; + ot->idname = "PALETTE_OT_color_delete"; + + /* api callbacks */ + ot->exec = palette_color_delete_exec; + ot->poll = palette_poll; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int brush_reset_exec(bContext *C, wmOperator *UNUSED(op)) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - Object *ob = CTX_data_active_object(C); - - if (!ob || !brush) return OPERATOR_CANCELLED; - - /* TODO: other modes */ - if (ob->mode & OB_MODE_SCULPT) { - BKE_brush_sculpt_reset(brush); - } - else { - return OPERATOR_CANCELLED; - } - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - - return OPERATOR_FINISHED; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + Object *ob = CTX_data_active_object(C); + + if (!ob || !brush) + return OPERATOR_CANCELLED; + + /* TODO: other modes */ + if (ob->mode & OB_MODE_SCULPT) { + BKE_brush_sculpt_reset(brush); + } + else { + return OPERATOR_CANCELLED; + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + + return OPERATOR_FINISHED; } static void BRUSH_OT_reset(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Reset Brush"; - ot->description = "Return brush to defaults based on current tool"; - ot->idname = "BRUSH_OT_reset"; + /* identifiers */ + ot->name = "Reset Brush"; + ot->description = "Return brush to defaults based on current tool"; + ot->idname = "BRUSH_OT_reset"; - /* api callbacks */ - ot->exec = brush_reset_exec; + /* api callbacks */ + ot->exec = brush_reset_exec; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int brush_tool(const Brush *brush, size_t tool_offset) { - return *(((char *)brush) + tool_offset); + return *(((char *)brush) + tool_offset); } static void brush_tool_set(const Brush *brush, size_t tool_offset, int tool) { - *(((char *)brush) + tool_offset) = tool; + *(((char *)brush) + tool_offset) = tool; } static Brush *brush_tool_cycle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool) { - Brush *brush, *first_brush; - - if (!brush_orig && !(brush_orig = bmain->brushes.first)) { - return NULL; - } - - if (brush_tool(brush_orig, paint->runtime.tool_offset) != tool) { - /* If current brush's tool is different from what we need, - * start cycling from the beginning of the list. - * Such logic will activate the same exact brush not relating from - * which tool user requests other tool. - */ - - /* Try to tool-slot first. */ - first_brush = BKE_paint_toolslots_brush_get(paint, tool); - if (first_brush == NULL) { - first_brush = bmain->brushes.first; - } - } - else { - /* If user wants to switch to brush with the same tool as - * currently active brush do a cycling via all possible - * brushes with requested tool. - */ - first_brush = brush_orig->id.next ? brush_orig->id.next : bmain->brushes.first; - } - - /* get the next brush with the active tool */ - brush = first_brush; - do { - if ((brush->ob_mode & paint->runtime.ob_mode) && - (brush_tool(brush, paint->runtime.tool_offset) == tool)) - { - return brush; - } - - brush = brush->id.next ? brush->id.next : bmain->brushes.first; - } while (brush != first_brush); - - return NULL; + Brush *brush, *first_brush; + + if (!brush_orig && !(brush_orig = bmain->brushes.first)) { + return NULL; + } + + if (brush_tool(brush_orig, paint->runtime.tool_offset) != tool) { + /* If current brush's tool is different from what we need, + * start cycling from the beginning of the list. + * Such logic will activate the same exact brush not relating from + * which tool user requests other tool. + */ + + /* Try to tool-slot first. */ + first_brush = BKE_paint_toolslots_brush_get(paint, tool); + if (first_brush == NULL) { + first_brush = bmain->brushes.first; + } + } + else { + /* If user wants to switch to brush with the same tool as + * currently active brush do a cycling via all possible + * brushes with requested tool. + */ + first_brush = brush_orig->id.next ? brush_orig->id.next : bmain->brushes.first; + } + + /* get the next brush with the active tool */ + brush = first_brush; + do { + if ((brush->ob_mode & paint->runtime.ob_mode) && + (brush_tool(brush, paint->runtime.tool_offset) == tool)) { + return brush; + } + + brush = brush->id.next ? brush->id.next : bmain->brushes.first; + } while (brush != first_brush); + + return NULL; } static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool) { - if (!brush_orig || brush_tool(brush_orig, paint->runtime.tool_offset) != tool) { - Brush *br; - /* if the current brush is not using the desired tool, look - * for one that is */ - br = brush_tool_cycle(bmain, paint, brush_orig, tool); - /* store the previously-selected brush */ - if (br) - br->toggle_brush = brush_orig; - - return br; - } - else if (brush_orig->toggle_brush) { - /* if current brush is using the desired tool, try to toggle - * back to the previously selected brush. */ - return brush_orig->toggle_brush; - } - else - return NULL; + if (!brush_orig || brush_tool(brush_orig, paint->runtime.tool_offset) != tool) { + Brush *br; + /* if the current brush is not using the desired tool, look + * for one that is */ + br = brush_tool_cycle(bmain, paint, brush_orig, tool); + /* store the previously-selected brush */ + if (br) + br->toggle_brush = brush_orig; + + return br; + } + else if (brush_orig->toggle_brush) { + /* if current brush is using the desired tool, try to toggle + * back to the previously selected brush. */ + return brush_orig->toggle_brush; + } + else + return NULL; } -static int brush_generic_tool_set( - Main *bmain, Paint *paint, const int tool, - const char *tool_name, const bool create_missing, - const bool toggle) +static int brush_generic_tool_set(Main *bmain, + Paint *paint, + const int tool, + const char *tool_name, + const bool create_missing, + const bool toggle) { - Brush *brush, *brush_orig = BKE_paint_brush(paint); - - if (toggle) { - brush = brush_tool_toggle(bmain, paint, brush_orig, tool); - } - else { - brush = brush_tool_cycle(bmain, paint, brush_orig, tool); - } - - if (!brush && brush_tool(brush_orig, paint->runtime.tool_offset) != tool && create_missing) { - brush = BKE_brush_add(bmain, tool_name, paint->runtime.ob_mode); - id_us_min(&brush->id); /* fake user only */ - brush_tool_set(brush, paint->runtime.tool_offset, tool); - brush->toggle_brush = brush_orig; - } - - if (brush) { - BKE_paint_brush_set(paint, brush); - BKE_paint_invalidate_overlay_all(); - - WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Brush *brush, *brush_orig = BKE_paint_brush(paint); + + if (toggle) { + brush = brush_tool_toggle(bmain, paint, brush_orig, tool); + } + else { + brush = brush_tool_cycle(bmain, paint, brush_orig, tool); + } + + if (!brush && brush_tool(brush_orig, paint->runtime.tool_offset) != tool && create_missing) { + brush = BKE_brush_add(bmain, tool_name, paint->runtime.ob_mode); + id_us_min(&brush->id); /* fake user only */ + brush_tool_set(brush, paint->runtime.tool_offset, tool); + brush->toggle_brush = brush_orig; + } + + if (brush) { + BKE_paint_brush_set(paint, brush); + BKE_paint_invalidate_overlay_all(); + + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } static const ePaintMode brush_select_paint_modes[] = { - PAINT_MODE_SCULPT, - PAINT_MODE_VERTEX, - PAINT_MODE_WEIGHT, - PAINT_MODE_TEXTURE_3D, - PAINT_MODE_GPENCIL, + PAINT_MODE_SCULPT, + PAINT_MODE_VERTEX, + PAINT_MODE_WEIGHT, + PAINT_MODE_TEXTURE_3D, + PAINT_MODE_GPENCIL, }; static int brush_select_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - const bool create_missing = RNA_boolean_get(op->ptr, "create_missing"); - const bool toggle = RNA_boolean_get(op->ptr, "toggle"); - const char *tool_name = "Brush"; - int tool = 0; - - ePaintMode paint_mode = PAINT_MODE_INVALID; - for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) { - paint_mode = brush_select_paint_modes[i]; - const char *op_prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); - PropertyRNA *prop = RNA_struct_find_property(op->ptr, op_prop_id); - if (RNA_property_is_set(op->ptr, prop)) { - tool = RNA_property_enum_get(op->ptr, prop); - break; - } - } - - if (paint_mode == PAINT_MODE_INVALID) { - return OPERATOR_CANCELLED; - } - - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); - RNA_enum_name_from_value(items, tool, &tool_name); - return brush_generic_tool_set( - bmain, paint, tool, - tool_name, create_missing, - toggle); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const bool create_missing = RNA_boolean_get(op->ptr, "create_missing"); + const bool toggle = RNA_boolean_get(op->ptr, "toggle"); + const char *tool_name = "Brush"; + int tool = 0; + + ePaintMode paint_mode = PAINT_MODE_INVALID; + for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) { + paint_mode = brush_select_paint_modes[i]; + const char *op_prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); + PropertyRNA *prop = RNA_struct_find_property(op->ptr, op_prop_id); + if (RNA_property_is_set(op->ptr, prop)) { + tool = RNA_property_enum_get(op->ptr, prop); + break; + } + } + + if (paint_mode == PAINT_MODE_INVALID) { + return OPERATOR_CANCELLED; + } + + Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); + const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); + RNA_enum_name_from_value(items, tool, &tool_name); + return brush_generic_tool_set(bmain, paint, tool, tool_name, create_missing, toggle); } static void PAINT_OT_brush_select(wmOperatorType *ot) { - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Brush Select"; - ot->description = "Select a paint mode's brush by tool type"; - ot->idname = "PAINT_OT_brush_select"; - - /* api callbacks */ - ot->exec = brush_select_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - /* All properties are hidden, so as not to show the redo panel. */ - for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) { - const ePaintMode paint_mode = brush_select_paint_modes[i]; - const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); - prop = RNA_def_enum(ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, ""); - RNA_def_property_flag(prop, PROP_HIDDEN); - } - - prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "Toggle between two brushes rather than cycling"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "create_missing", 0, "Create Missing", "If the requested brush type does not exist, create a new brush"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Brush Select"; + ot->description = "Select a paint mode's brush by tool type"; + ot->idname = "PAINT_OT_brush_select"; + + /* api callbacks */ + ot->exec = brush_select_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + /* All properties are hidden, so as not to show the redo panel. */ + for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) { + const ePaintMode paint_mode = brush_select_paint_modes[i]; + const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); + prop = RNA_def_enum( + ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, ""); + RNA_def_property_flag(prop, PROP_HIDDEN); + } + + prop = RNA_def_boolean( + ot->srna, "toggle", 0, "Toggle", "Toggle between two brushes rather than cycling"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, + "create_missing", + 0, + "Create Missing", + "If the requested brush type does not exist, create a new brush"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } static int brush_uv_sculpt_tool_set_exec(bContext *C, wmOperator *op) { - Brush *brush; - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - ts->uv_sculpt_tool = RNA_enum_get(op->ptr, "tool"); - brush = ts->uvsculpt->paint.brush; - /* To update toolshelf */ - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); - - return OPERATOR_FINISHED; + Brush *brush; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + ts->uv_sculpt_tool = RNA_enum_get(op->ptr, "tool"); + brush = ts->uvsculpt->paint.brush; + /* To update toolshelf */ + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + + return OPERATOR_FINISHED; } static void BRUSH_OT_uv_sculpt_tool_set(wmOperatorType *ot) { - /* identifiers */ - ot->name = "UV Sculpt Tool Set"; - ot->description = "Set the UV sculpt tool"; - ot->idname = "BRUSH_OT_uv_sculpt_tool_set"; + /* identifiers */ + ot->name = "UV Sculpt Tool Set"; + ot->description = "Set the UV sculpt tool"; + ot->idname = "BRUSH_OT_uv_sculpt_tool_set"; - /* api callbacks */ - ot->exec = brush_uv_sculpt_tool_set_exec; - ot->poll = uv_sculpt_poll; + /* api callbacks */ + ot->exec = brush_uv_sculpt_tool_set_exec; + ot->poll = uv_sculpt_poll; - /* flags */ - ot->flag = 0; + /* flags */ + ot->flag = 0; - /* props */ - ot->prop = RNA_def_enum(ot->srna, "tool", rna_enum_uv_sculpt_tool_items, 0, "Tool", ""); + /* props */ + ot->prop = RNA_def_enum(ot->srna, "tool", rna_enum_uv_sculpt_tool_items, 0, "Tool", ""); } /***** Stencil Control *****/ typedef enum { - STENCIL_TRANSLATE, - STENCIL_SCALE, - STENCIL_ROTATE, + STENCIL_TRANSLATE, + STENCIL_SCALE, + STENCIL_ROTATE, } StencilControlMode; typedef enum { - STENCIL_PRIMARY = 0, - STENCIL_SECONDARY = 1, + STENCIL_PRIMARY = 0, + STENCIL_SECONDARY = 1, } StencilTextureMode; - typedef enum { - STENCIL_CONSTRAINT_X = 1, - STENCIL_CONSTRAINT_Y = 2, + STENCIL_CONSTRAINT_X = 1, + STENCIL_CONSTRAINT_Y = 2, } StencilConstraint; typedef struct { - float init_mouse[2]; - float init_spos[2]; - float init_sdim[2]; - float init_rot; - float init_angle; - float lenorig; - float area_size[2]; - StencilControlMode mode; - StencilConstraint constrain_mode; - int mask; /* we are twaking mask or colour stencil */ - Brush *br; - float *dim_target; - float *rot_target; - float *pos_target; - short event_type; + float init_mouse[2]; + float init_spos[2]; + float init_sdim[2]; + float init_rot; + float init_angle; + float lenorig; + float area_size[2]; + StencilControlMode mode; + StencilConstraint constrain_mode; + int mask; /* we are twaking mask or colour stencil */ + Brush *br; + float *dim_target; + float *rot_target; + float *pos_target; + short event_type; } StencilControlData; static void stencil_set_target(StencilControlData *scd) { - Brush *br = scd->br; - float mdiff[2]; - if (scd->mask) { - copy_v2_v2(scd->init_sdim, br->mask_stencil_dimension); - copy_v2_v2(scd->init_spos, br->mask_stencil_pos); - scd->init_rot = br->mask_mtex.rot; - - scd->dim_target = br->mask_stencil_dimension; - scd->rot_target = &br->mask_mtex.rot; - scd->pos_target = br->mask_stencil_pos; - - sub_v2_v2v2(mdiff, scd->init_mouse, br->mask_stencil_pos); - } - else { - copy_v2_v2(scd->init_sdim, br->stencil_dimension); - copy_v2_v2(scd->init_spos, br->stencil_pos); - scd->init_rot = br->mtex.rot; - - scd->dim_target = br->stencil_dimension; - scd->rot_target = &br->mtex.rot; - scd->pos_target = br->stencil_pos; - - sub_v2_v2v2(mdiff, scd->init_mouse, br->stencil_pos); - } - - scd->lenorig = len_v2(mdiff); - - scd->init_angle = atan2f(mdiff[1], mdiff[0]); + Brush *br = scd->br; + float mdiff[2]; + if (scd->mask) { + copy_v2_v2(scd->init_sdim, br->mask_stencil_dimension); + copy_v2_v2(scd->init_spos, br->mask_stencil_pos); + scd->init_rot = br->mask_mtex.rot; + + scd->dim_target = br->mask_stencil_dimension; + scd->rot_target = &br->mask_mtex.rot; + scd->pos_target = br->mask_stencil_pos; + + sub_v2_v2v2(mdiff, scd->init_mouse, br->mask_stencil_pos); + } + else { + copy_v2_v2(scd->init_sdim, br->stencil_dimension); + copy_v2_v2(scd->init_spos, br->stencil_pos); + scd->init_rot = br->mtex.rot; + + scd->dim_target = br->stencil_dimension; + scd->rot_target = &br->mtex.rot; + scd->pos_target = br->stencil_pos; + + sub_v2_v2v2(mdiff, scd->init_mouse, br->stencil_pos); + } + + scd->lenorig = len_v2(mdiff); + + scd->init_angle = atan2f(mdiff[1], mdiff[0]); } static int stencil_control_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); - float mvalf[2] = {event->mval[0], event->mval[1]}; - ARegion *ar = CTX_wm_region(C); - StencilControlData *scd; - int mask = RNA_enum_get(op->ptr, "texmode"); - - if (mask) { - if (br->mask_mtex.brush_map_mode != MTEX_MAP_MODE_STENCIL) - return OPERATOR_CANCELLED; - } - else { - if (br->mtex.brush_map_mode != MTEX_MAP_MODE_STENCIL) - return OPERATOR_CANCELLED; - } - - scd = MEM_mallocN(sizeof(StencilControlData), "stencil_control"); - scd->mask = mask; - scd->br = br; - - copy_v2_v2(scd->init_mouse, mvalf); - - stencil_set_target(scd); - - scd->mode = RNA_enum_get(op->ptr, "mode"); - scd->event_type = event->type; - scd->area_size[0] = ar->winx; - scd->area_size[1] = ar->winy; - - - op->customdata = scd; - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *br = BKE_paint_brush(paint); + float mvalf[2] = {event->mval[0], event->mval[1]}; + ARegion *ar = CTX_wm_region(C); + StencilControlData *scd; + int mask = RNA_enum_get(op->ptr, "texmode"); + + if (mask) { + if (br->mask_mtex.brush_map_mode != MTEX_MAP_MODE_STENCIL) + return OPERATOR_CANCELLED; + } + else { + if (br->mtex.brush_map_mode != MTEX_MAP_MODE_STENCIL) + return OPERATOR_CANCELLED; + } + + scd = MEM_mallocN(sizeof(StencilControlData), "stencil_control"); + scd->mask = mask; + scd->br = br; + + copy_v2_v2(scd->init_mouse, mvalf); + + stencil_set_target(scd); + + scd->mode = RNA_enum_get(op->ptr, "mode"); + scd->event_type = event->type; + scd->area_size[0] = ar->winx; + scd->area_size[1] = ar->winy; + + op->customdata = scd; + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; } static void stencil_restore(StencilControlData *scd) { - copy_v2_v2(scd->dim_target, scd->init_sdim); - copy_v2_v2(scd->pos_target, scd->init_spos); - *scd->rot_target = scd->init_rot; + copy_v2_v2(scd->dim_target, scd->init_sdim); + copy_v2_v2(scd->pos_target, scd->init_spos); + *scd->rot_target = scd->init_rot; } static void stencil_control_cancel(bContext *UNUSED(C), wmOperator *op) { - StencilControlData *scd = op->customdata; + StencilControlData *scd = op->customdata; - stencil_restore(scd); - MEM_freeN(op->customdata); + stencil_restore(scd); + MEM_freeN(op->customdata); } static void stencil_control_calculate(StencilControlData *scd, const int mval[2]) { #define PIXEL_MARGIN 5 - float mdiff[2]; - float mvalf[2] = {mval[0], mval[1]}; - switch (scd->mode) { - case STENCIL_TRANSLATE: - sub_v2_v2v2(mdiff, mvalf, scd->init_mouse); - add_v2_v2v2(scd->pos_target, scd->init_spos, mdiff); - CLAMP(scd->pos_target[0], - -scd->dim_target[0] + PIXEL_MARGIN, - scd->area_size[0] + scd->dim_target[0] - PIXEL_MARGIN); - - CLAMP(scd->pos_target[1], - -scd->dim_target[1] + PIXEL_MARGIN, - scd->area_size[1] + scd->dim_target[1] - PIXEL_MARGIN); - - break; - case STENCIL_SCALE: - { - float len, factor; - sub_v2_v2v2(mdiff, mvalf, scd->pos_target); - len = len_v2(mdiff); - factor = len / scd->lenorig; - copy_v2_v2(mdiff, scd->init_sdim); - if (scd->constrain_mode != STENCIL_CONSTRAINT_Y) - mdiff[0] = factor * scd->init_sdim[0]; - if (scd->constrain_mode != STENCIL_CONSTRAINT_X) - mdiff[1] = factor * scd->init_sdim[1]; - CLAMP(mdiff[0], 5.0f, 10000.0f); - CLAMP(mdiff[1], 5.0f, 10000.0f); - copy_v2_v2(scd->dim_target, mdiff); - break; - } - case STENCIL_ROTATE: - { - float angle; - sub_v2_v2v2(mdiff, mvalf, scd->pos_target); - angle = atan2f(mdiff[1], mdiff[0]); - angle = scd->init_rot + angle - scd->init_angle; - if (angle < 0.0f) - angle += (float)(2 * M_PI); - if (angle > (float)(2 * M_PI)) - angle -= (float)(2 * M_PI); - *scd->rot_target = angle; - break; - } - } + float mdiff[2]; + float mvalf[2] = {mval[0], mval[1]}; + switch (scd->mode) { + case STENCIL_TRANSLATE: + sub_v2_v2v2(mdiff, mvalf, scd->init_mouse); + add_v2_v2v2(scd->pos_target, scd->init_spos, mdiff); + CLAMP(scd->pos_target[0], + -scd->dim_target[0] + PIXEL_MARGIN, + scd->area_size[0] + scd->dim_target[0] - PIXEL_MARGIN); + + CLAMP(scd->pos_target[1], + -scd->dim_target[1] + PIXEL_MARGIN, + scd->area_size[1] + scd->dim_target[1] - PIXEL_MARGIN); + + break; + case STENCIL_SCALE: { + float len, factor; + sub_v2_v2v2(mdiff, mvalf, scd->pos_target); + len = len_v2(mdiff); + factor = len / scd->lenorig; + copy_v2_v2(mdiff, scd->init_sdim); + if (scd->constrain_mode != STENCIL_CONSTRAINT_Y) + mdiff[0] = factor * scd->init_sdim[0]; + if (scd->constrain_mode != STENCIL_CONSTRAINT_X) + mdiff[1] = factor * scd->init_sdim[1]; + CLAMP(mdiff[0], 5.0f, 10000.0f); + CLAMP(mdiff[1], 5.0f, 10000.0f); + copy_v2_v2(scd->dim_target, mdiff); + break; + } + case STENCIL_ROTATE: { + float angle; + sub_v2_v2v2(mdiff, mvalf, scd->pos_target); + angle = atan2f(mdiff[1], mdiff[0]); + angle = scd->init_rot + angle - scd->init_angle; + if (angle < 0.0f) + angle += (float)(2 * M_PI); + if (angle > (float)(2 * M_PI)) + angle -= (float)(2 * M_PI); + *scd->rot_target = angle; + break; + } + } #undef PIXEL_MARGIN } static int stencil_control_modal(bContext *C, wmOperator *op, const wmEvent *event) { - StencilControlData *scd = op->customdata; - - if (event->type == scd->event_type && event->val == KM_RELEASE) { - MEM_freeN(op->customdata); - WM_event_add_notifier(C, NC_WINDOW, NULL); - return OPERATOR_FINISHED; - } - - switch (event->type) { - case MOUSEMOVE: - stencil_control_calculate(scd, event->mval); - break; - case ESCKEY: - if (event->val == KM_PRESS) { - stencil_control_cancel(C, op); - WM_event_add_notifier(C, NC_WINDOW, NULL); - return OPERATOR_CANCELLED; - } - break; - case XKEY: - if (event->val == KM_PRESS) { - - if (scd->constrain_mode == STENCIL_CONSTRAINT_X) - scd->constrain_mode = 0; - else - scd->constrain_mode = STENCIL_CONSTRAINT_X; - - stencil_control_calculate(scd, event->mval); - } - break; - case YKEY: - if (event->val == KM_PRESS) { - if (scd->constrain_mode == STENCIL_CONSTRAINT_Y) - scd->constrain_mode = 0; - else - scd->constrain_mode = STENCIL_CONSTRAINT_Y; - - stencil_control_calculate(scd, event->mval); - } - break; - default: - break; - } - - ED_region_tag_redraw(CTX_wm_region(C)); - - return OPERATOR_RUNNING_MODAL; + StencilControlData *scd = op->customdata; + + if (event->type == scd->event_type && event->val == KM_RELEASE) { + MEM_freeN(op->customdata); + WM_event_add_notifier(C, NC_WINDOW, NULL); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case MOUSEMOVE: + stencil_control_calculate(scd, event->mval); + break; + case ESCKEY: + if (event->val == KM_PRESS) { + stencil_control_cancel(C, op); + WM_event_add_notifier(C, NC_WINDOW, NULL); + return OPERATOR_CANCELLED; + } + break; + case XKEY: + if (event->val == KM_PRESS) { + + if (scd->constrain_mode == STENCIL_CONSTRAINT_X) + scd->constrain_mode = 0; + else + scd->constrain_mode = STENCIL_CONSTRAINT_X; + + stencil_control_calculate(scd, event->mval); + } + break; + case YKEY: + if (event->val == KM_PRESS) { + if (scd->constrain_mode == STENCIL_CONSTRAINT_Y) + scd->constrain_mode = 0; + else + scd->constrain_mode = STENCIL_CONSTRAINT_Y; + + stencil_control_calculate(scd, event->mval); + } + break; + default: + break; + } + + ED_region_tag_redraw(CTX_wm_region(C)); + + return OPERATOR_RUNNING_MODAL; } static bool stencil_control_poll(bContext *C) { - ePaintMode mode = BKE_paintmode_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); - Paint *paint; - Brush *br; + Paint *paint; + Brush *br; - if (!paint_supports_texture(mode)) - return false; + if (!paint_supports_texture(mode)) + return false; - paint = BKE_paint_get_active_from_context(C); - br = BKE_paint_brush(paint); - return (br && - (br->mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL || - br->mask_mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL)); + paint = BKE_paint_get_active_from_context(C); + br = BKE_paint_brush(paint); + return (br && (br->mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL || + br->mask_mtex.brush_map_mode == MTEX_MAP_MODE_STENCIL)); } static void BRUSH_OT_stencil_control(wmOperatorType *ot) { - static const EnumPropertyItem stencil_control_items[] = { - {STENCIL_TRANSLATE, "TRANSLATION", 0, "Translation", ""}, - {STENCIL_SCALE, "SCALE", 0, "Scale", ""}, - {STENCIL_ROTATE, "ROTATION", 0, "Rotation", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem stencil_texture_items[] = { - {STENCIL_PRIMARY, "PRIMARY", 0, "Primary", ""}, - {STENCIL_SECONDARY, "SECONDARY", 0, "Secondary", ""}, - {0, NULL, 0, NULL, NULL}, - }; - /* identifiers */ - ot->name = "Stencil Brush Control"; - ot->description = "Control the stencil brush"; - ot->idname = "BRUSH_OT_stencil_control"; - - /* api callbacks */ - ot->invoke = stencil_control_invoke; - ot->modal = stencil_control_modal; - ot->cancel = stencil_control_cancel; - ot->poll = stencil_control_poll; - - /* flags */ - ot->flag = 0; - - PropertyRNA *prop; - prop = RNA_def_enum(ot->srna, "mode", stencil_control_items, STENCIL_TRANSLATE, "Tool", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_enum(ot->srna, "texmode", stencil_texture_items, STENCIL_PRIMARY, "Tool", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + static const EnumPropertyItem stencil_control_items[] = { + {STENCIL_TRANSLATE, "TRANSLATION", 0, "Translation", ""}, + {STENCIL_SCALE, "SCALE", 0, "Scale", ""}, + {STENCIL_ROTATE, "ROTATION", 0, "Rotation", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem stencil_texture_items[] = { + {STENCIL_PRIMARY, "PRIMARY", 0, "Primary", ""}, + {STENCIL_SECONDARY, "SECONDARY", 0, "Secondary", ""}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ + ot->name = "Stencil Brush Control"; + ot->description = "Control the stencil brush"; + ot->idname = "BRUSH_OT_stencil_control"; + + /* api callbacks */ + ot->invoke = stencil_control_invoke; + ot->modal = stencil_control_modal; + ot->cancel = stencil_control_cancel; + ot->poll = stencil_control_poll; + + /* flags */ + ot->flag = 0; + + PropertyRNA *prop; + prop = RNA_def_enum(ot->srna, "mode", stencil_control_items, STENCIL_TRANSLATE, "Tool", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "texmode", stencil_texture_items, STENCIL_PRIMARY, "Tool", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } - static int stencil_fit_image_aspect_exec(bContext *C, wmOperator *op) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); - bool use_scale = RNA_boolean_get(op->ptr, "use_scale"); - bool use_repeat = RNA_boolean_get(op->ptr, "use_repeat"); - bool do_mask = RNA_boolean_get(op->ptr, "mask"); - Tex *tex = NULL; - MTex *mtex = NULL; - if (br) { - mtex = do_mask ? &br->mask_mtex : &br->mtex; - tex = mtex->tex; - } - - if (tex && tex->type == TEX_IMAGE && tex->ima) { - float aspx, aspy; - Image *ima = tex->ima; - float orig_area, stencil_area, factor; - ED_image_get_uv_aspect(ima, NULL, &aspx, &aspy); - - if (use_scale) { - aspx *= mtex->size[0]; - aspy *= mtex->size[1]; - } - - if (use_repeat && tex->extend == TEX_REPEAT) { - aspx *= tex->xrepeat; - aspy *= tex->yrepeat; - } - - orig_area = aspx * aspy; - - if (do_mask) { - stencil_area = br->mask_stencil_dimension[0] * br->mask_stencil_dimension[1]; - } - else { - stencil_area = br->stencil_dimension[0] * br->stencil_dimension[1]; - } - - factor = sqrtf(stencil_area / orig_area); - - if (do_mask) { - br->mask_stencil_dimension[0] = factor * aspx; - br->mask_stencil_dimension[1] = factor * aspy; - - } - else { - br->stencil_dimension[0] = factor * aspx; - br->stencil_dimension[1] = factor * aspy; - } - } - - WM_event_add_notifier(C, NC_WINDOW, NULL); - - return OPERATOR_FINISHED; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *br = BKE_paint_brush(paint); + bool use_scale = RNA_boolean_get(op->ptr, "use_scale"); + bool use_repeat = RNA_boolean_get(op->ptr, "use_repeat"); + bool do_mask = RNA_boolean_get(op->ptr, "mask"); + Tex *tex = NULL; + MTex *mtex = NULL; + if (br) { + mtex = do_mask ? &br->mask_mtex : &br->mtex; + tex = mtex->tex; + } + + if (tex && tex->type == TEX_IMAGE && tex->ima) { + float aspx, aspy; + Image *ima = tex->ima; + float orig_area, stencil_area, factor; + ED_image_get_uv_aspect(ima, NULL, &aspx, &aspy); + + if (use_scale) { + aspx *= mtex->size[0]; + aspy *= mtex->size[1]; + } + + if (use_repeat && tex->extend == TEX_REPEAT) { + aspx *= tex->xrepeat; + aspy *= tex->yrepeat; + } + + orig_area = aspx * aspy; + + if (do_mask) { + stencil_area = br->mask_stencil_dimension[0] * br->mask_stencil_dimension[1]; + } + else { + stencil_area = br->stencil_dimension[0] * br->stencil_dimension[1]; + } + + factor = sqrtf(stencil_area / orig_area); + + if (do_mask) { + br->mask_stencil_dimension[0] = factor * aspx; + br->mask_stencil_dimension[1] = factor * aspy; + } + else { + br->stencil_dimension[0] = factor * aspx; + br->stencil_dimension[1] = factor * aspy; + } + } + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; } - static void BRUSH_OT_stencil_fit_image_aspect(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Image Aspect"; - ot->description = "When using an image texture, adjust the stencil size to fit the image aspect ratio"; - ot->idname = "BRUSH_OT_stencil_fit_image_aspect"; - - /* api callbacks */ - ot->exec = stencil_fit_image_aspect_exec; - ot->poll = stencil_control_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_boolean(ot->srna, "use_repeat", 1, "Use Repeat", "Use repeat mapping values"); - RNA_def_boolean(ot->srna, "use_scale", 1, "Use Scale", "Use texture scale values"); - RNA_def_boolean(ot->srna, "mask", 0, "Modify Mask Stencil", "Modify either the primary or mask stencil"); + /* identifiers */ + ot->name = "Image Aspect"; + ot->description = + "When using an image texture, adjust the stencil size to fit the image aspect ratio"; + ot->idname = "BRUSH_OT_stencil_fit_image_aspect"; + + /* api callbacks */ + ot->exec = stencil_fit_image_aspect_exec; + ot->poll = stencil_control_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "use_repeat", 1, "Use Repeat", "Use repeat mapping values"); + RNA_def_boolean(ot->srna, "use_scale", 1, "Use Scale", "Use texture scale values"); + RNA_def_boolean( + ot->srna, "mask", 0, "Modify Mask Stencil", "Modify either the primary or mask stencil"); } - static int stencil_reset_transform_exec(bContext *C, wmOperator *op) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *br = BKE_paint_brush(paint); - bool do_mask = RNA_boolean_get(op->ptr, "mask"); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *br = BKE_paint_brush(paint); + bool do_mask = RNA_boolean_get(op->ptr, "mask"); - if (!br) - return OPERATOR_CANCELLED; + if (!br) + return OPERATOR_CANCELLED; - if (do_mask) { - br->mask_stencil_pos[0] = 256; - br->mask_stencil_pos[1] = 256; + if (do_mask) { + br->mask_stencil_pos[0] = 256; + br->mask_stencil_pos[1] = 256; - br->mask_stencil_dimension[0] = 256; - br->mask_stencil_dimension[1] = 256; + br->mask_stencil_dimension[0] = 256; + br->mask_stencil_dimension[1] = 256; - br->mask_mtex.rot = 0; - } - else { - br->stencil_pos[0] = 256; - br->stencil_pos[1] = 256; + br->mask_mtex.rot = 0; + } + else { + br->stencil_pos[0] = 256; + br->stencil_pos[1] = 256; - br->stencil_dimension[0] = 256; - br->stencil_dimension[1] = 256; + br->stencil_dimension[0] = 256; + br->stencil_dimension[1] = 256; - br->mtex.rot = 0; - } + br->mtex.rot = 0; + } - WM_event_add_notifier(C, NC_WINDOW, NULL); + WM_event_add_notifier(C, NC_WINDOW, NULL); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } - static void BRUSH_OT_stencil_reset_transform(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Reset Transform"; - ot->description = "Reset the stencil transformation to the default"; - ot->idname = "BRUSH_OT_stencil_reset_transform"; + /* identifiers */ + ot->name = "Reset Transform"; + ot->description = "Reset the stencil transformation to the default"; + ot->idname = "BRUSH_OT_stencil_reset_transform"; - /* api callbacks */ - ot->exec = stencil_reset_transform_exec; - ot->poll = stencil_control_poll; + /* api callbacks */ + ot->exec = stencil_reset_transform_exec; + ot->poll = stencil_control_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "mask", 0, "Modify Mask Stencil", "Modify either the primary or mask stencil"); + RNA_def_boolean( + ot->srna, "mask", 0, "Modify Mask Stencil", "Modify either the primary or mask stencil"); } - /**************************** registration **********************************/ void ED_operatormacros_paint(void) { - wmOperatorType *ot; - wmOperatorTypeMacro *otmacro; - - ot = WM_operatortype_append_macro( - "PAINTCURVE_OT_add_point_slide", "Add Curve Point and Slide", - "Add new curve point and slide it", OPTYPE_UNDO); - ot->description = "Add new curve point and slide it"; - WM_operatortype_macro_define(ot, "PAINTCURVE_OT_add_point"); - otmacro = WM_operatortype_macro_define(ot, "PAINTCURVE_OT_slide"); - RNA_boolean_set(otmacro->ptr, "align", true); - RNA_boolean_set(otmacro->ptr, "select", false); + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("PAINTCURVE_OT_add_point_slide", + "Add Curve Point and Slide", + "Add new curve point and slide it", + OPTYPE_UNDO); + ot->description = "Add new curve point and slide it"; + WM_operatortype_macro_define(ot, "PAINTCURVE_OT_add_point"); + otmacro = WM_operatortype_macro_define(ot, "PAINTCURVE_OT_slide"); + RNA_boolean_set(otmacro->ptr, "align", true); + RNA_boolean_set(otmacro->ptr, "select", false); } - void ED_operatortypes_paint(void) { - /* palette */ - WM_operatortype_append(PALETTE_OT_new); - WM_operatortype_append(PALETTE_OT_color_add); - WM_operatortype_append(PALETTE_OT_color_delete); - - /* paint curve */ - WM_operatortype_append(PAINTCURVE_OT_new); - WM_operatortype_append(PAINTCURVE_OT_add_point); - WM_operatortype_append(PAINTCURVE_OT_delete_point); - WM_operatortype_append(PAINTCURVE_OT_select); - WM_operatortype_append(PAINTCURVE_OT_slide); - WM_operatortype_append(PAINTCURVE_OT_draw); - WM_operatortype_append(PAINTCURVE_OT_cursor); - - /* brush */ - WM_operatortype_append(BRUSH_OT_add); - WM_operatortype_append(BRUSH_OT_add_gpencil); - WM_operatortype_append(BRUSH_OT_scale_size); - WM_operatortype_append(BRUSH_OT_curve_preset); - WM_operatortype_append(BRUSH_OT_reset); - WM_operatortype_append(BRUSH_OT_stencil_control); - WM_operatortype_append(BRUSH_OT_stencil_fit_image_aspect); - WM_operatortype_append(BRUSH_OT_stencil_reset_transform); - - /* note, particle uses a different system, can be added with existing operators in wm.py */ - WM_operatortype_append(PAINT_OT_brush_select); - WM_operatortype_append(BRUSH_OT_uv_sculpt_tool_set); - - /* image */ - WM_operatortype_append(PAINT_OT_texture_paint_toggle); - WM_operatortype_append(PAINT_OT_image_paint); - WM_operatortype_append(PAINT_OT_sample_color); - WM_operatortype_append(PAINT_OT_grab_clone); - WM_operatortype_append(PAINT_OT_project_image); - WM_operatortype_append(PAINT_OT_image_from_view); - WM_operatortype_append(PAINT_OT_brush_colors_flip); - WM_operatortype_append(PAINT_OT_add_texture_paint_slot); - WM_operatortype_append(PAINT_OT_add_simple_uvs); - - /* weight */ - WM_operatortype_append(PAINT_OT_weight_paint_toggle); - WM_operatortype_append(PAINT_OT_weight_paint); - WM_operatortype_append(PAINT_OT_weight_set); - WM_operatortype_append(PAINT_OT_weight_from_bones); - WM_operatortype_append(PAINT_OT_weight_gradient); - WM_operatortype_append(PAINT_OT_weight_sample); - WM_operatortype_append(PAINT_OT_weight_sample_group); - - /* uv */ - WM_operatortype_append(SCULPT_OT_uv_sculpt_stroke); - - /* vertex selection */ - WM_operatortype_append(PAINT_OT_vert_select_all); - WM_operatortype_append(PAINT_OT_vert_select_ungrouped); - - /* vertex */ - WM_operatortype_append(PAINT_OT_vertex_paint_toggle); - WM_operatortype_append(PAINT_OT_vertex_paint); - WM_operatortype_append(PAINT_OT_vertex_color_set); - WM_operatortype_append(PAINT_OT_vertex_color_smooth); - - WM_operatortype_append(PAINT_OT_vertex_color_brightness_contrast); - WM_operatortype_append(PAINT_OT_vertex_color_hsv); - WM_operatortype_append(PAINT_OT_vertex_color_invert); - WM_operatortype_append(PAINT_OT_vertex_color_levels); - WM_operatortype_append(PAINT_OT_vertex_color_from_weight); - - /* face-select */ - WM_operatortype_append(PAINT_OT_face_select_linked); - WM_operatortype_append(PAINT_OT_face_select_linked_pick); - WM_operatortype_append(PAINT_OT_face_select_all); - WM_operatortype_append(PAINT_OT_face_select_hide); - WM_operatortype_append(PAINT_OT_face_select_reveal); - - /* partial visibility */ - WM_operatortype_append(PAINT_OT_hide_show); - - /* paint masking */ - WM_operatortype_append(PAINT_OT_mask_flood_fill); - WM_operatortype_append(PAINT_OT_mask_lasso_gesture); + /* palette */ + WM_operatortype_append(PALETTE_OT_new); + WM_operatortype_append(PALETTE_OT_color_add); + WM_operatortype_append(PALETTE_OT_color_delete); + + /* paint curve */ + WM_operatortype_append(PAINTCURVE_OT_new); + WM_operatortype_append(PAINTCURVE_OT_add_point); + WM_operatortype_append(PAINTCURVE_OT_delete_point); + WM_operatortype_append(PAINTCURVE_OT_select); + WM_operatortype_append(PAINTCURVE_OT_slide); + WM_operatortype_append(PAINTCURVE_OT_draw); + WM_operatortype_append(PAINTCURVE_OT_cursor); + + /* brush */ + WM_operatortype_append(BRUSH_OT_add); + WM_operatortype_append(BRUSH_OT_add_gpencil); + WM_operatortype_append(BRUSH_OT_scale_size); + WM_operatortype_append(BRUSH_OT_curve_preset); + WM_operatortype_append(BRUSH_OT_reset); + WM_operatortype_append(BRUSH_OT_stencil_control); + WM_operatortype_append(BRUSH_OT_stencil_fit_image_aspect); + WM_operatortype_append(BRUSH_OT_stencil_reset_transform); + + /* note, particle uses a different system, can be added with existing operators in wm.py */ + WM_operatortype_append(PAINT_OT_brush_select); + WM_operatortype_append(BRUSH_OT_uv_sculpt_tool_set); + + /* image */ + WM_operatortype_append(PAINT_OT_texture_paint_toggle); + WM_operatortype_append(PAINT_OT_image_paint); + WM_operatortype_append(PAINT_OT_sample_color); + WM_operatortype_append(PAINT_OT_grab_clone); + WM_operatortype_append(PAINT_OT_project_image); + WM_operatortype_append(PAINT_OT_image_from_view); + WM_operatortype_append(PAINT_OT_brush_colors_flip); + WM_operatortype_append(PAINT_OT_add_texture_paint_slot); + WM_operatortype_append(PAINT_OT_add_simple_uvs); + + /* weight */ + WM_operatortype_append(PAINT_OT_weight_paint_toggle); + WM_operatortype_append(PAINT_OT_weight_paint); + WM_operatortype_append(PAINT_OT_weight_set); + WM_operatortype_append(PAINT_OT_weight_from_bones); + WM_operatortype_append(PAINT_OT_weight_gradient); + WM_operatortype_append(PAINT_OT_weight_sample); + WM_operatortype_append(PAINT_OT_weight_sample_group); + + /* uv */ + WM_operatortype_append(SCULPT_OT_uv_sculpt_stroke); + + /* vertex selection */ + WM_operatortype_append(PAINT_OT_vert_select_all); + WM_operatortype_append(PAINT_OT_vert_select_ungrouped); + + /* vertex */ + WM_operatortype_append(PAINT_OT_vertex_paint_toggle); + WM_operatortype_append(PAINT_OT_vertex_paint); + WM_operatortype_append(PAINT_OT_vertex_color_set); + WM_operatortype_append(PAINT_OT_vertex_color_smooth); + + WM_operatortype_append(PAINT_OT_vertex_color_brightness_contrast); + WM_operatortype_append(PAINT_OT_vertex_color_hsv); + WM_operatortype_append(PAINT_OT_vertex_color_invert); + WM_operatortype_append(PAINT_OT_vertex_color_levels); + WM_operatortype_append(PAINT_OT_vertex_color_from_weight); + + /* face-select */ + WM_operatortype_append(PAINT_OT_face_select_linked); + WM_operatortype_append(PAINT_OT_face_select_linked_pick); + WM_operatortype_append(PAINT_OT_face_select_all); + WM_operatortype_append(PAINT_OT_face_select_hide); + WM_operatortype_append(PAINT_OT_face_select_reveal); + + /* partial visibility */ + WM_operatortype_append(PAINT_OT_hide_show); + + /* paint masking */ + WM_operatortype_append(PAINT_OT_mask_flood_fill); + WM_operatortype_append(PAINT_OT_mask_lasso_gesture); } void ED_keymap_paint(wmKeyConfig *keyconf) { - wmKeyMap *keymap; + wmKeyMap *keymap; - keymap = WM_keymap_ensure(keyconf, "Paint Curve", 0, 0); - keymap->poll = paint_curve_poll; + keymap = WM_keymap_ensure(keyconf, "Paint Curve", 0, 0); + keymap->poll = paint_curve_poll; - /* Sculpt mode */ - keymap = WM_keymap_ensure(keyconf, "Sculpt", 0, 0); - keymap->poll = sculpt_mode_poll; + /* Sculpt mode */ + keymap = WM_keymap_ensure(keyconf, "Sculpt", 0, 0); + keymap->poll = sculpt_mode_poll; - /* Vertex Paint mode */ - keymap = WM_keymap_ensure(keyconf, "Vertex Paint", 0, 0); - keymap->poll = vertex_paint_mode_poll; + /* Vertex Paint mode */ + keymap = WM_keymap_ensure(keyconf, "Vertex Paint", 0, 0); + keymap->poll = vertex_paint_mode_poll; - /* Weight Paint mode */ - keymap = WM_keymap_ensure(keyconf, "Weight Paint", 0, 0); - keymap->poll = weight_paint_mode_poll; + /* Weight Paint mode */ + keymap = WM_keymap_ensure(keyconf, "Weight Paint", 0, 0); + keymap->poll = weight_paint_mode_poll; - /*Weight paint's Vertex Selection Mode */ - keymap = WM_keymap_ensure(keyconf, "Weight Paint Vertex Selection", 0, 0); - keymap->poll = vert_paint_poll; + /*Weight paint's Vertex Selection Mode */ + keymap = WM_keymap_ensure(keyconf, "Weight Paint Vertex Selection", 0, 0); + keymap->poll = vert_paint_poll; - /* Image/Texture Paint mode */ - keymap = WM_keymap_ensure(keyconf, "Image Paint", 0, 0); - keymap->poll = image_texture_paint_poll; + /* Image/Texture Paint mode */ + keymap = WM_keymap_ensure(keyconf, "Image Paint", 0, 0); + keymap->poll = image_texture_paint_poll; - /* face-mask mode */ - keymap = WM_keymap_ensure(keyconf, "Face Mask", 0, 0); - keymap->poll = facemask_paint_poll; + /* face-mask mode */ + keymap = WM_keymap_ensure(keyconf, "Face Mask", 0, 0); + keymap->poll = facemask_paint_poll; - keymap = WM_keymap_ensure(keyconf, "UV Sculpt", 0, 0); - keymap->poll = uv_sculpt_keymap_poll; + keymap = WM_keymap_ensure(keyconf, "UV Sculpt", 0, 0); + keymap->poll = uv_sculpt_keymap_poll; - /* paint stroke */ - keymap = paint_stroke_modal_keymap(keyconf); - WM_modalkeymap_assign(keymap, "SCULPT_OT_brush_stroke"); + /* paint stroke */ + keymap = paint_stroke_modal_keymap(keyconf); + WM_modalkeymap_assign(keymap, "SCULPT_OT_brush_stroke"); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index d66224034eb..4a586f4f7df 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -21,7 +21,6 @@ * \ingroup edsculpt */ - #include "MEM_guardedalloc.h" #include "BLI_math.h" @@ -49,7 +48,6 @@ #include "WM_api.h" #include "WM_types.h" - #include "GPU_immediate.h" #include "GPU_state.h" @@ -70,844 +68,847 @@ #endif typedef struct PaintSample { - float mouse[2]; - float pressure; + float mouse[2]; + float pressure; } PaintSample; typedef struct PaintStroke { - void *mode_data; - void *stroke_cursor; - wmTimer *timer; - struct RNG *rng; - - /* Cached values */ - ViewContext vc; - Brush *brush; - UnifiedPaintSettings *ups; - - /* used for lines and curves */ - ListBase line; - - /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs - * to smooth the stroke */ - PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; - int num_samples; - int cur_sample; - - float last_mouse_position[2]; - /* space distance covered so far */ - float stroke_distance; - - /* Set whether any stroke step has yet occurred - * e.g. in sculpt mode, stroke doesn't start until cursor - * passes over the mesh */ - bool stroke_started; - /* Set when enough motion was found for rake rotation */ - bool rake_started; - /* event that started stroke, for modal() return */ - int event_type; - /* check if stroke variables have been initialized */ - bool stroke_init; - /* check if various brush mapping variables have been initialized */ - bool brush_init; - float initial_mouse[2]; - /* cached_pressure stores initial pressure for size pressure influence mainly */ - float cached_size_pressure; - /* last pressure will store last pressure value for use in interpolation for space strokes */ - float last_pressure; - int stroke_mode; - - float zoom_2d; - int pen_flip; - - /* line constraint */ - bool constrain_line; - float constrained_pos[2]; - - StrokeGetLocation get_location; - StrokeTestStart test_start; - StrokeUpdateStep update_step; - StrokeRedraw redraw; - StrokeDone done; + void *mode_data; + void *stroke_cursor; + wmTimer *timer; + struct RNG *rng; + + /* Cached values */ + ViewContext vc; + Brush *brush; + UnifiedPaintSettings *ups; + + /* used for lines and curves */ + ListBase line; + + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs + * to smooth the stroke */ + PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; + int num_samples; + int cur_sample; + + float last_mouse_position[2]; + /* space distance covered so far */ + float stroke_distance; + + /* Set whether any stroke step has yet occurred + * e.g. in sculpt mode, stroke doesn't start until cursor + * passes over the mesh */ + bool stroke_started; + /* Set when enough motion was found for rake rotation */ + bool rake_started; + /* event that started stroke, for modal() return */ + int event_type; + /* check if stroke variables have been initialized */ + bool stroke_init; + /* check if various brush mapping variables have been initialized */ + bool brush_init; + float initial_mouse[2]; + /* cached_pressure stores initial pressure for size pressure influence mainly */ + float cached_size_pressure; + /* last pressure will store last pressure value for use in interpolation for space strokes */ + float last_pressure; + int stroke_mode; + + float zoom_2d; + int pen_flip; + + /* line constraint */ + bool constrain_line; + float constrained_pos[2]; + + StrokeGetLocation get_location; + StrokeTestStart test_start; + StrokeUpdateStep update_step; + StrokeRedraw redraw; + StrokeDone done; } PaintStroke; /*** Cursors ***/ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) { - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - PaintStroke *stroke = customdata; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + PaintStroke *stroke = customdata; - if (stroke && brush) { - GPU_line_smooth(true); - GPU_blend(true); + if (stroke && brush) { + GPU_line_smooth(true); + GPU_blend(true); - ARegion *ar = stroke->vc.ar; + ARegion *ar = stroke->vc.ar; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor4ubv(paint->paint_cursor_col); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor4ubv(paint->paint_cursor_col); - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, x, y); - immVertex2f(pos, - stroke->last_mouse_position[0] + ar->winrct.xmin, - stroke->last_mouse_position[1] + ar->winrct.ymin); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, x, y); + immVertex2f(pos, + stroke->last_mouse_position[0] + ar->winrct.xmin, + stroke->last_mouse_position[1] + ar->winrct.ymin); - immEnd(); + immEnd(); - immUnbindProgram(); + immUnbindProgram(); - GPU_blend(false); - GPU_line_smooth(false); - } + GPU_blend(false); + GPU_line_smooth(false); + } } static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) { - Paint *paint = BKE_paint_get_active_from_context(C); - PaintStroke *stroke = customdata; + Paint *paint = BKE_paint_get_active_from_context(C); + PaintStroke *stroke = customdata; - GPU_line_smooth(true); + GPU_line_smooth(true); - uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint shdr_pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - immUniform1i("colors_len", 2); /* "advanced" mode */ - const float alpha = (float)paint->paint_cursor_col[3] / 255.0f; - immUniformArray4fv("colors", (float *)(float[][4]){{0.0f, 0.0f, 0.0f, alpha}, {1.0f, 1.0f, 1.0f, alpha}}, 2); - immUniform1f("dash_width", 6.0f); + immUniform1i("colors_len", 2); /* "advanced" mode */ + const float alpha = (float)paint->paint_cursor_col[3] / 255.0f; + immUniformArray4fv( + "colors", (float *)(float[][4]){{0.0f, 0.0f, 0.0f, alpha}, {1.0f, 1.0f, 1.0f, alpha}}, 2); + immUniform1f("dash_width", 6.0f); - immBegin(GPU_PRIM_LINES, 2); + immBegin(GPU_PRIM_LINES, 2); - ARegion *ar = stroke->vc.ar; + ARegion *ar = stroke->vc.ar; - if (stroke->constrain_line) { - immVertex2f(shdr_pos, - stroke->last_mouse_position[0] + ar->winrct.xmin, - stroke->last_mouse_position[1] + ar->winrct.ymin); + if (stroke->constrain_line) { + immVertex2f(shdr_pos, + stroke->last_mouse_position[0] + ar->winrct.xmin, + stroke->last_mouse_position[1] + ar->winrct.ymin); - immVertex2f(shdr_pos, - stroke->constrained_pos[0] + ar->winrct.xmin, - stroke->constrained_pos[1] + ar->winrct.ymin); - } - else { - immVertex2f(shdr_pos, - stroke->last_mouse_position[0] + ar->winrct.xmin, - stroke->last_mouse_position[1] + ar->winrct.ymin); + immVertex2f(shdr_pos, + stroke->constrained_pos[0] + ar->winrct.xmin, + stroke->constrained_pos[1] + ar->winrct.ymin); + } + else { + immVertex2f(shdr_pos, + stroke->last_mouse_position[0] + ar->winrct.xmin, + stroke->last_mouse_position[1] + ar->winrct.ymin); - immVertex2f(shdr_pos, x, y); - } + immVertex2f(shdr_pos, x, y); + } - immEnd(); + immEnd(); - immUnbindProgram(); + immUnbindProgram(); - GPU_line_smooth(false); + GPU_line_smooth(false); } static bool paint_tool_require_location(Brush *brush, ePaintMode mode) { - switch (mode) { - case PAINT_MODE_SCULPT: - if (ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, - SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)) - { - return false; - } - else { - return true; - } - default: - break; - } - - return true; + switch (mode) { + case PAINT_MODE_SCULPT: + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_THUMB)) { + return false; + } + else { + return true; + } + default: + break; + } + + return true; } /* Initialize the stroke cache variants from operator properties */ -static bool paint_brush_update( - bContext *C, - Brush *brush, - ePaintMode mode, - struct PaintStroke *stroke, - const float mouse_init[2], - float mouse[2], float pressure, - float r_location[3], bool *r_location_is_set) +static bool paint_brush_update(bContext *C, + Brush *brush, + ePaintMode mode, + struct PaintStroke *stroke, + const float mouse_init[2], + float mouse[2], + float pressure, + float r_location[3], + bool *r_location_is_set) { - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = stroke->ups; - bool location_sampled = false; - bool location_success = false; - /* Use to perform all operations except applying the stroke, - * needed for operations that require cursor motion (rake). */ - bool is_dry_run = false; - bool do_random = false; - bool do_random_mask = false; - *r_location_is_set = false; - /* XXX: Use pressure value from first brush step for brushes which don't - * support strokes (grab, thumb). They depends on initial state and - * brush coord/pressure/etc. - * It's more an events design issue, which doesn't split coordinate/pressure/angle - * changing events. We should avoid this after events system re-design */ - if (!stroke->brush_init) { - copy_v2_v2(stroke->initial_mouse, mouse); - copy_v2_v2(ups->last_rake, mouse); - copy_v2_v2(ups->tex_mouse, mouse); - copy_v2_v2(ups->mask_tex_mouse, mouse); - stroke->cached_size_pressure = pressure; - - ups->do_linear_conversion = false; - ups->colorspace = NULL; - - /* check here if color sampling the main brush should do color conversion. This is done here - * to avoid locking up to get the image buffer during sampling */ - if (brush->mtex.tex && brush->mtex.tex->type == TEX_IMAGE && brush->mtex.tex->ima) { - ImBuf *tex_ibuf = BKE_image_pool_acquire_ibuf(brush->mtex.tex->ima, &brush->mtex.tex->iuser, NULL); - if (tex_ibuf && tex_ibuf->rect_float == NULL) { - ups->do_linear_conversion = true; - ups->colorspace = tex_ibuf->rect_colorspace; - } - BKE_image_pool_release_ibuf(brush->mtex.tex->ima, tex_ibuf, NULL); - } - - stroke->brush_init = true; - } - - if (paint_supports_dynamic_size(brush, mode)) { - copy_v2_v2(ups->tex_mouse, mouse); - copy_v2_v2(ups->mask_tex_mouse, mouse); - stroke->cached_size_pressure = pressure; - } - - /* Truly temporary data that isn't stored in properties */ - - ups->stroke_active = true; - ups->size_pressure_value = stroke->cached_size_pressure; - - ups->pixel_radius = BKE_brush_size_get(scene, brush); - - if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, mode)) { - ups->pixel_radius *= stroke->cached_size_pressure; - } - - if (paint_supports_dynamic_tex_coords(brush, mode)) { - - if (ELEM(brush->mtex.brush_map_mode, - MTEX_MAP_MODE_VIEW, - MTEX_MAP_MODE_AREA, - MTEX_MAP_MODE_RANDOM)) - { - do_random = true; - } - - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - BKE_brush_randomize_texture_coords(ups, false); - else { - copy_v2_v2(ups->tex_mouse, mouse); - } - - /* take care of mask texture, if any */ - if (brush->mask_mtex.tex) { - - if (ELEM(brush->mask_mtex.brush_map_mode, - MTEX_MAP_MODE_VIEW, - MTEX_MAP_MODE_AREA, - MTEX_MAP_MODE_RANDOM)) - { - do_random_mask = true; - } - - if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - BKE_brush_randomize_texture_coords(ups, true); - else { - copy_v2_v2(ups->mask_tex_mouse, mouse); - } - } - } - - - if (brush->flag & BRUSH_ANCHORED) { - bool hit = false; - float halfway[2]; - - const float dx = mouse[0] - stroke->initial_mouse[0]; - const float dy = mouse[1] - stroke->initial_mouse[1]; - - ups->anchored_size = ups->pixel_radius = sqrtf(dx * dx + dy * dy); - - ups->brush_rotation = ups->brush_rotation_sec = atan2f(dx, dy) + (float)M_PI; - - if (brush->flag & BRUSH_EDGE_TO_EDGE) { - halfway[0] = dx * 0.5f + stroke->initial_mouse[0]; - halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; - - if (stroke->get_location) { - if (stroke->get_location(C, r_location, halfway)) { - hit = true; - location_sampled = true; - location_success = true; - *r_location_is_set = true; - } - else if (!paint_tool_require_location(brush, mode)) { - hit = true; - } - } - else { - hit = true; - } - } - if (hit) { - copy_v2_v2(ups->anchored_initial_mouse, halfway); - copy_v2_v2(ups->tex_mouse, halfway); - copy_v2_v2(ups->mask_tex_mouse, halfway); - copy_v2_v2(mouse, halfway); - ups->anchored_size /= 2.0f; - ups->pixel_radius /= 2.0f; - stroke->stroke_distance = ups->pixel_radius; - } - else { - copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse); - copy_v2_v2(mouse, stroke->initial_mouse); - stroke->stroke_distance = ups->pixel_radius; - } - ups->pixel_radius /= stroke->zoom_2d; - ups->draw_anchored = true; - } - else { - /* here we are using the initial mouse coordinate because we do not want the rake - * result to depend on jittering */ - if (!stroke->brush_init) { - copy_v2_v2(ups->last_rake, mouse_init); - } - /* curve strokes do their own rake calculation */ - else if (!(brush->flag & BRUSH_CURVE)) { - if (!paint_calculate_rake_rotation(ups, brush, mouse_init)) { - /* Not enough motion to define an angle. */ - if (!stroke->rake_started) { - is_dry_run = true; - } - } - else { - stroke->rake_started = true; - } - } - } - - if ((do_random || do_random_mask) && stroke->rng == NULL) { - /* Lazy initialization. */ - uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= (uint)POINTER_AS_INT(brush); - stroke->rng = BLI_rng_new(rng_seed); - } - - if (do_random) { - if (brush->mtex.brush_angle_mode & MTEX_ANGLE_RANDOM) { - ups->brush_rotation += -brush->mtex.random_angle / 2.0f + - brush->mtex.random_angle * BLI_rng_get_float(stroke->rng); - } - } - - if (do_random_mask) { - if (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RANDOM) { - ups->brush_rotation_sec += -brush->mask_mtex.random_angle / 2.0f + - brush->mask_mtex.random_angle * BLI_rng_get_float(stroke->rng); - } - } - - if (!location_sampled) { - if (stroke->get_location) { - if (stroke->get_location(C, r_location, mouse)) { - location_success = true; - *r_location_is_set = true; - } - else if (!paint_tool_require_location(brush, mode)) - location_success = true; - } - else { - zero_v3(r_location); - location_success = true; - /* don't set 'r_location_is_set', since we don't want to use the value. */ - } - } - - return location_success && (is_dry_run == false); + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = stroke->ups; + bool location_sampled = false; + bool location_success = false; + /* Use to perform all operations except applying the stroke, + * needed for operations that require cursor motion (rake). */ + bool is_dry_run = false; + bool do_random = false; + bool do_random_mask = false; + *r_location_is_set = false; + /* XXX: Use pressure value from first brush step for brushes which don't + * support strokes (grab, thumb). They depends on initial state and + * brush coord/pressure/etc. + * It's more an events design issue, which doesn't split coordinate/pressure/angle + * changing events. We should avoid this after events system re-design */ + if (!stroke->brush_init) { + copy_v2_v2(stroke->initial_mouse, mouse); + copy_v2_v2(ups->last_rake, mouse); + copy_v2_v2(ups->tex_mouse, mouse); + copy_v2_v2(ups->mask_tex_mouse, mouse); + stroke->cached_size_pressure = pressure; + + ups->do_linear_conversion = false; + ups->colorspace = NULL; + + /* check here if color sampling the main brush should do color conversion. This is done here + * to avoid locking up to get the image buffer during sampling */ + if (brush->mtex.tex && brush->mtex.tex->type == TEX_IMAGE && brush->mtex.tex->ima) { + ImBuf *tex_ibuf = BKE_image_pool_acquire_ibuf( + brush->mtex.tex->ima, &brush->mtex.tex->iuser, NULL); + if (tex_ibuf && tex_ibuf->rect_float == NULL) { + ups->do_linear_conversion = true; + ups->colorspace = tex_ibuf->rect_colorspace; + } + BKE_image_pool_release_ibuf(brush->mtex.tex->ima, tex_ibuf, NULL); + } + + stroke->brush_init = true; + } + + if (paint_supports_dynamic_size(brush, mode)) { + copy_v2_v2(ups->tex_mouse, mouse); + copy_v2_v2(ups->mask_tex_mouse, mouse); + stroke->cached_size_pressure = pressure; + } + + /* Truly temporary data that isn't stored in properties */ + + ups->stroke_active = true; + ups->size_pressure_value = stroke->cached_size_pressure; + + ups->pixel_radius = BKE_brush_size_get(scene, brush); + + if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, mode)) { + ups->pixel_radius *= stroke->cached_size_pressure; + } + + if (paint_supports_dynamic_tex_coords(brush, mode)) { + + if (ELEM(brush->mtex.brush_map_mode, + MTEX_MAP_MODE_VIEW, + MTEX_MAP_MODE_AREA, + MTEX_MAP_MODE_RANDOM)) { + do_random = true; + } + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + BKE_brush_randomize_texture_coords(ups, false); + else { + copy_v2_v2(ups->tex_mouse, mouse); + } + + /* take care of mask texture, if any */ + if (brush->mask_mtex.tex) { + + if (ELEM(brush->mask_mtex.brush_map_mode, + MTEX_MAP_MODE_VIEW, + MTEX_MAP_MODE_AREA, + MTEX_MAP_MODE_RANDOM)) { + do_random_mask = true; + } + + if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + BKE_brush_randomize_texture_coords(ups, true); + else { + copy_v2_v2(ups->mask_tex_mouse, mouse); + } + } + } + + if (brush->flag & BRUSH_ANCHORED) { + bool hit = false; + float halfway[2]; + + const float dx = mouse[0] - stroke->initial_mouse[0]; + const float dy = mouse[1] - stroke->initial_mouse[1]; + + ups->anchored_size = ups->pixel_radius = sqrtf(dx * dx + dy * dy); + + ups->brush_rotation = ups->brush_rotation_sec = atan2f(dx, dy) + (float)M_PI; + + if (brush->flag & BRUSH_EDGE_TO_EDGE) { + halfway[0] = dx * 0.5f + stroke->initial_mouse[0]; + halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; + + if (stroke->get_location) { + if (stroke->get_location(C, r_location, halfway)) { + hit = true; + location_sampled = true; + location_success = true; + *r_location_is_set = true; + } + else if (!paint_tool_require_location(brush, mode)) { + hit = true; + } + } + else { + hit = true; + } + } + if (hit) { + copy_v2_v2(ups->anchored_initial_mouse, halfway); + copy_v2_v2(ups->tex_mouse, halfway); + copy_v2_v2(ups->mask_tex_mouse, halfway); + copy_v2_v2(mouse, halfway); + ups->anchored_size /= 2.0f; + ups->pixel_radius /= 2.0f; + stroke->stroke_distance = ups->pixel_radius; + } + else { + copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse); + copy_v2_v2(mouse, stroke->initial_mouse); + stroke->stroke_distance = ups->pixel_radius; + } + ups->pixel_radius /= stroke->zoom_2d; + ups->draw_anchored = true; + } + else { + /* here we are using the initial mouse coordinate because we do not want the rake + * result to depend on jittering */ + if (!stroke->brush_init) { + copy_v2_v2(ups->last_rake, mouse_init); + } + /* curve strokes do their own rake calculation */ + else if (!(brush->flag & BRUSH_CURVE)) { + if (!paint_calculate_rake_rotation(ups, brush, mouse_init)) { + /* Not enough motion to define an angle. */ + if (!stroke->rake_started) { + is_dry_run = true; + } + } + else { + stroke->rake_started = true; + } + } + } + + if ((do_random || do_random_mask) && stroke->rng == NULL) { + /* Lazy initialization. */ + uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); + rng_seed ^= (uint)POINTER_AS_INT(brush); + stroke->rng = BLI_rng_new(rng_seed); + } + + if (do_random) { + if (brush->mtex.brush_angle_mode & MTEX_ANGLE_RANDOM) { + ups->brush_rotation += -brush->mtex.random_angle / 2.0f + + brush->mtex.random_angle * BLI_rng_get_float(stroke->rng); + } + } + + if (do_random_mask) { + if (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RANDOM) { + ups->brush_rotation_sec += -brush->mask_mtex.random_angle / 2.0f + + brush->mask_mtex.random_angle * BLI_rng_get_float(stroke->rng); + } + } + + if (!location_sampled) { + if (stroke->get_location) { + if (stroke->get_location(C, r_location, mouse)) { + location_success = true; + *r_location_is_set = true; + } + else if (!paint_tool_require_location(brush, mode)) + location_success = true; + } + else { + zero_v3(r_location); + location_success = true; + /* don't set 'r_location_is_set', since we don't want to use the value. */ + } + } + + return location_success && (is_dry_run == false); } static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert) { - bool use_jitter = (brush->flag & BRUSH_ABSOLUTE_JITTER) ? - (brush->jitter_absolute != 0) : (brush->jitter != 0); - - /* jitter-ed brush gives weird and unpredictable result for this - * kinds of stroke, so manually disable jitter usage (sergey) */ - use_jitter &= (brush->flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0; - use_jitter &= (!ELEM(mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) || - !(invert && brush->imagepaint_tool == PAINT_TOOL_CLONE)); + bool use_jitter = (brush->flag & BRUSH_ABSOLUTE_JITTER) ? (brush->jitter_absolute != 0) : + (brush->jitter != 0); + /* jitter-ed brush gives weird and unpredictable result for this + * kinds of stroke, so manually disable jitter usage (sergey) */ + use_jitter &= (brush->flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0; + use_jitter &= (!ELEM(mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D) || + !(invert && brush->imagepaint_tool == PAINT_TOOL_CLONE)); - return use_jitter; + return use_jitter; } /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ -static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float mouse_in[2], float pressure) +static void paint_brush_stroke_add_step(bContext *C, + wmOperator *op, + const float mouse_in[2], + float pressure) { - Scene *scene = CTX_data_scene(C); - Paint *paint = BKE_paint_get_active_from_context(C); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - PaintStroke *stroke = op->customdata; - UnifiedPaintSettings *ups = stroke->ups; - float mouse_out[2]; - PointerRNA itemptr; - float location[3]; + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + PaintStroke *stroke = op->customdata; + UnifiedPaintSettings *ups = stroke->ups; + float mouse_out[2]; + PointerRNA itemptr; + float location[3]; /* the following code is adapted from texture paint. It may not be needed but leaving here * just in case for reference (code in texpaint removed as part of refactoring). * It's strange that only texpaint had these guards. */ #if 0 - /* special exception here for too high pressure values on first touch in - * windows for some tablets, then we just skip first touch .. */ - if (tablet && (pressure >= 0.99f) && - ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || - BKE_brush_use_alpha_pressure(scene, pop->s.brush) || - BKE_brush_use_size_pressure(scene, pop->s.brush))) - { - return; - } - - /* This can be removed once fixed properly in - * BKE_brush_painter_paint( - * BrushPainter *painter, BrushFunc func, - * float *pos, double time, float pressure, void *user); - * at zero pressure we should do nothing 1/2^12 is 0.0002 - * which is the sensitivity of the most sensitive pen tablet available */ - if (tablet && (pressure < 0.0002f) && - ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || - BKE_brush_use_alpha_pressure(scene, pop->s.brush) || - BKE_brush_use_size_pressure(scene, pop->s.brush))) - { - return; - } + /* special exception here for too high pressure values on first touch in + * windows for some tablets, then we just skip first touch .. */ + if (tablet && (pressure >= 0.99f) && + ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || + BKE_brush_use_alpha_pressure(scene, pop->s.brush) || + BKE_brush_use_size_pressure(scene, pop->s.brush))) + { + return; + } + + /* This can be removed once fixed properly in + * BKE_brush_painter_paint( + * BrushPainter *painter, BrushFunc func, + * float *pos, double time, float pressure, void *user); + * at zero pressure we should do nothing 1/2^12 is 0.0002 + * which is the sensitivity of the most sensitive pen tablet available */ + if (tablet && (pressure < 0.0002f) && + ((pop->s.brush->flag & BRUSH_SPACING_PRESSURE) || + BKE_brush_use_alpha_pressure(scene, pop->s.brush) || + BKE_brush_use_size_pressure(scene, pop->s.brush))) + { + return; + } #endif - /* copy last position -before- jittering, or space fill code - * will create too many dabs */ - copy_v2_v2(stroke->last_mouse_position, mouse_in); - stroke->last_pressure = pressure; - - if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { - float delta[2]; - float factor = stroke->zoom_2d; - - if (brush->flag & BRUSH_JITTER_PRESSURE) - factor *= pressure; - - BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out); - - /* XXX: meh, this is round about because - * BKE_brush_jitter_pos isn't written in the best way to - * be reused here */ - if (factor != 1.0f) { - sub_v2_v2v2(delta, mouse_out, mouse_in); - mul_v2_fl(delta, factor); - add_v2_v2v2(mouse_out, mouse_in, delta); - } - } - else { - copy_v2_v2(mouse_out, mouse_in); - } - - - bool is_location_is_set; - ups->last_hit = paint_brush_update( - C, brush, mode, stroke, mouse_in, mouse_out, pressure, - location, &is_location_is_set); - if (is_location_is_set) { - copy_v3_v3(ups->last_location, location); - } - if (!ups->last_hit) { - return; - } - - /* Add to stroke */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - RNA_float_set(&itemptr, "size", ups->pixel_radius); - RNA_float_set_array(&itemptr, "location", location); - RNA_float_set_array(&itemptr, "mouse", mouse_out); - RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); - RNA_float_set(&itemptr, "pressure", pressure); - - stroke->update_step(C, stroke, &itemptr); - - /* don't record this for now, it takes up a lot of memory when doing long - * strokes with small brush size, and operators have register disabled */ - RNA_collection_clear(op->ptr, "stroke"); + /* copy last position -before- jittering, or space fill code + * will create too many dabs */ + copy_v2_v2(stroke->last_mouse_position, mouse_in); + stroke->last_pressure = pressure; + + if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { + float delta[2]; + float factor = stroke->zoom_2d; + + if (brush->flag & BRUSH_JITTER_PRESSURE) + factor *= pressure; + + BKE_brush_jitter_pos(scene, brush, mouse_in, mouse_out); + + /* XXX: meh, this is round about because + * BKE_brush_jitter_pos isn't written in the best way to + * be reused here */ + if (factor != 1.0f) { + sub_v2_v2v2(delta, mouse_out, mouse_in); + mul_v2_fl(delta, factor); + add_v2_v2v2(mouse_out, mouse_in, delta); + } + } + else { + copy_v2_v2(mouse_out, mouse_in); + } + + bool is_location_is_set; + ups->last_hit = paint_brush_update( + C, brush, mode, stroke, mouse_in, mouse_out, pressure, location, &is_location_is_set); + if (is_location_is_set) { + copy_v3_v3(ups->last_location, location); + } + if (!ups->last_hit) { + return; + } + + /* Add to stroke */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + RNA_float_set(&itemptr, "size", ups->pixel_radius); + RNA_float_set_array(&itemptr, "location", location); + RNA_float_set_array(&itemptr, "mouse", mouse_out); + RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); + RNA_float_set(&itemptr, "pressure", pressure); + + stroke->update_step(C, stroke, &itemptr); + + /* don't record this for now, it takes up a lot of memory when doing long + * strokes with small brush size, and operators have register disabled */ + RNA_collection_clear(op->ptr, "stroke"); } /* Returns zero if no sculpt changes should be made, non-zero otherwise */ -static bool paint_smooth_stroke( - PaintStroke *stroke, const PaintSample *sample, ePaintMode mode, - float r_mouse[2], float *r_pressure) +static bool paint_smooth_stroke(PaintStroke *stroke, + const PaintSample *sample, + ePaintMode mode, + float r_mouse[2], + float *r_pressure) { - if (paint_supports_smooth_stroke(stroke->brush, mode)) { - float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d; - float u = stroke->brush->smooth_stroke_factor; - - /* If the mouse is moving within the radius of the last move, - * don't update the mouse position. This allows sharp turns. */ - if (len_squared_v2v2(stroke->last_mouse_position, sample->mouse) < SQUARE(radius)) { - return false; - } - - interp_v2_v2v2(r_mouse, sample->mouse, stroke->last_mouse_position, u); - *r_pressure = interpf(sample->pressure, stroke->last_pressure, u); - } - else { - r_mouse[0] = sample->mouse[0]; - r_mouse[1] = sample->mouse[1]; - *r_pressure = sample->pressure; - } - - return true; + if (paint_supports_smooth_stroke(stroke->brush, mode)) { + float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d; + float u = stroke->brush->smooth_stroke_factor; + + /* If the mouse is moving within the radius of the last move, + * don't update the mouse position. This allows sharp turns. */ + if (len_squared_v2v2(stroke->last_mouse_position, sample->mouse) < SQUARE(radius)) { + return false; + } + + interp_v2_v2v2(r_mouse, sample->mouse, stroke->last_mouse_position, u); + *r_pressure = interpf(sample->pressure, stroke->last_pressure, u); + } + else { + r_mouse[0] = sample->mouse[0]; + r_mouse[1] = sample->mouse[1]; + *r_pressure = sample->pressure; + } + + return true; } -static float paint_space_stroke_spacing( - const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure) +static float paint_space_stroke_spacing(const Scene *scene, + PaintStroke *stroke, + float size_pressure, + float spacing_pressure) { - /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel - * causing very high step sizes, hanging blender [#32381] */ - const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure); - float spacing = stroke->brush->spacing; + /* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel + * causing very high step sizes, hanging blender [#32381] */ + const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure); + float spacing = stroke->brush->spacing; - /* apply spacing pressure */ - if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) - spacing = spacing * (1.5f - spacing_pressure); + /* apply spacing pressure */ + if (stroke->brush->flag & BRUSH_SPACING_PRESSURE) + spacing = spacing * (1.5f - spacing_pressure); - /* stroke system is used for 2d paint too, so we need to account for - * the fact that brush can be scaled there. */ - spacing *= stroke->zoom_2d; + /* stroke system is used for 2d paint too, so we need to account for + * the fact that brush can be scaled there. */ + spacing *= stroke->zoom_2d; - return max_ff(1.0, size_clamp * spacing / 50.0f); + return max_ff(1.0, size_clamp * spacing / 50.0f); } - - static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing) { - int i; - const int n = 100 / spacing; - const float h = spacing / 50.0f; - const float x0 = x - 1; + int i; + const int n = 100 / spacing; + const float h = spacing / 50.0f; + const float x0 = x - 1; - float sum; + float sum; - sum = 0; - for (i = 0; i < n; i++) { - float xx; + sum = 0; + for (i = 0; i < n; i++) { + float xx; - xx = fabsf(x0 + i * h); + xx = fabsf(x0 + i * h); - if (xx < 1.0f) - sum += BKE_brush_curve_strength(br, xx, 1); - } + if (xx < 1.0f) + sum += BKE_brush_curve_strength(br, xx, 1); + } - return sum; + return sum; } static float paint_stroke_integrate_overlap(Brush *br, float factor) { - int i; - int m; - float g; - float max; - - float spacing = br->spacing * factor; - - if (!(br->flag & BRUSH_SPACE_ATTEN && (br->spacing < 100))) - return 1.0; - - m = 10; - g = 1.0f / m; - max = 0; - for (i = 0; i < m; i++) { - float overlap = fabs(paint_stroke_overlapped_curve(br, i * g, spacing)); - - if (overlap > max) - max = overlap; - } - - if (max == 0.0f) - return 1.0f; - else - return 1.0f / max; + int i; + int m; + float g; + float max; + + float spacing = br->spacing * factor; + + if (!(br->flag & BRUSH_SPACE_ATTEN && (br->spacing < 100))) + return 1.0; + + m = 10; + g = 1.0f / m; + max = 0; + for (i = 0; i < m; i++) { + float overlap = fabs(paint_stroke_overlapped_curve(br, i * g, spacing)); + + if (overlap > max) + max = overlap; + } + + if (max == 0.0f) + return 1.0f; + else + return 1.0f / max; } static float paint_space_stroke_spacing_variable( - const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length) + const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length) { - if (BKE_brush_use_size_pressure(scene, stroke->brush)) { - /* use pressure to modify size. set spacing so that at 100%, the circles - * are aligned nicely with no overlap. for this the spacing needs to be - * the average of the previous and next size. */ - float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); - float q = s * dpressure / (2.0f * length); - float pressure_fac = (1.0f + q) / (1.0f - q); - - float last_size_pressure = stroke->last_pressure; - float new_size_pressure = stroke->last_pressure * pressure_fac; - - /* average spacing */ - float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure); - float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure); - - return 0.5f * (last_spacing + new_spacing); - } - else { - /* no size pressure */ - return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); - } + if (BKE_brush_use_size_pressure(scene, stroke->brush)) { + /* use pressure to modify size. set spacing so that at 100%, the circles + * are aligned nicely with no overlap. for this the spacing needs to be + * the average of the previous and next size. */ + float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); + float q = s * dpressure / (2.0f * length); + float pressure_fac = (1.0f + q) / (1.0f - q); + + float last_size_pressure = stroke->last_pressure; + float new_size_pressure = stroke->last_pressure * pressure_fac; + + /* average spacing */ + float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure); + float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure); + + return 0.5f * (last_spacing + new_spacing); + } + else { + /* no size pressure */ + return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure); + } } /* For brushes with stroke spacing enabled, moves mouse in steps * towards the final mouse location. */ -static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mouse[2], float final_pressure) +static int paint_space_stroke(bContext *C, + wmOperator *op, + const float final_mouse[2], + float final_pressure) { - const Scene *scene = CTX_data_scene(C); - PaintStroke *stroke = op->customdata; - UnifiedPaintSettings *ups = stroke->ups; - int cnt = 0; + const Scene *scene = CTX_data_scene(C); + PaintStroke *stroke = op->customdata; + UnifiedPaintSettings *ups = stroke->ups; + int cnt = 0; - float pressure, dpressure; - float mouse[2], dmouse[2]; - float length; - float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); + float pressure, dpressure; + float mouse[2], dmouse[2]; + float length; + float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); - sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); + sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + pressure = stroke->last_pressure; + dpressure = final_pressure - stroke->last_pressure; - length = normalize_v2(dmouse); + length = normalize_v2(dmouse); - while (length > 0.0f) { - float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length); + while (length > 0.0f) { + float spacing = paint_space_stroke_spacing_variable( + scene, stroke, pressure, dpressure, length); - if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; - pressure = stroke->last_pressure + (spacing / length) * dpressure; + if (length >= spacing) { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + pressure = stroke->last_pressure + (spacing / length) * dpressure; - ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, spacing / no_pressure_spacing); + ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, + spacing / no_pressure_spacing); - stroke->stroke_distance += spacing / stroke->zoom_2d; - paint_brush_stroke_add_step(C, op, mouse, pressure); + stroke->stroke_distance += spacing / stroke->zoom_2d; + paint_brush_stroke_add_step(C, op, mouse, pressure); - length -= spacing; - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + length -= spacing; + pressure = stroke->last_pressure; + dpressure = final_pressure - stroke->last_pressure; - cnt++; - } - else { - break; - } - } + cnt++; + } + else { + break; + } + } - return cnt; + return cnt; } /**** Public API ****/ -PaintStroke *paint_stroke_new( - bContext *C, - wmOperator *op, - StrokeGetLocation get_location, - StrokeTestStart test_start, - StrokeUpdateStep update_step, - StrokeRedraw redraw, - StrokeDone done, int event_type) +PaintStroke *paint_stroke_new(bContext *C, + wmOperator *op, + StrokeGetLocation get_location, + StrokeTestStart test_start, + StrokeUpdateStep update_step, + StrokeRedraw redraw, + StrokeDone done, + int event_type) { - PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); - ToolSettings *toolsettings = CTX_data_tool_settings(C); - UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; - Paint *p = BKE_paint_get_active_from_context(C); - Brush *br = stroke->brush = BKE_paint_brush(p); - float zoomx, zoomy; - - ED_view3d_viewcontext_init(C, &stroke->vc); - - stroke->get_location = get_location; - stroke->test_start = test_start; - stroke->update_step = update_step; - stroke->redraw = redraw; - stroke->done = done; - stroke->event_type = event_type; /* for modal, return event */ - stroke->ups = ups; - stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); - - get_imapaint_zoom(C, &zoomx, &zoomy); - stroke->zoom_2d = max_ff(zoomx, zoomy); - - if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { - if (br->flag & (BRUSH_CURVE)) { - RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); - } - } - /* initialize here */ - ups->overlap_factor = 1.0; - ups->stroke_active = true; - - zero_v3(ups->average_stroke_accum); - ups->average_stroke_counter = 0; - - /* initialize here to avoid initialization conflict with threaded strokes */ - curvemapping_initialize(br->curve); - if (p->flags & PAINT_USE_CAVITY_MASK) - curvemapping_initialize(p->cavity_curve); - - BKE_paint_set_overlay_override(br->overlay_flags); - - return stroke; + PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke"); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = stroke->brush = BKE_paint_brush(p); + float zoomx, zoomy; + + ED_view3d_viewcontext_init(C, &stroke->vc); + + stroke->get_location = get_location; + stroke->test_start = test_start; + stroke->update_step = update_step; + stroke->redraw = redraw; + stroke->done = done; + stroke->event_type = event_type; /* for modal, return event */ + stroke->ups = ups; + stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); + + get_imapaint_zoom(C, &zoomx, &zoomy); + stroke->zoom_2d = max_ff(zoomx, zoomy); + + if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { + if (br->flag & (BRUSH_CURVE)) { + RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); + } + } + /* initialize here */ + ups->overlap_factor = 1.0; + ups->stroke_active = true; + + zero_v3(ups->average_stroke_accum); + ups->average_stroke_counter = 0; + + /* initialize here to avoid initialization conflict with threaded strokes */ + curvemapping_initialize(br->curve); + if (p->flags & PAINT_USE_CAVITY_MASK) + curvemapping_initialize(p->cavity_curve); + + BKE_paint_set_overlay_override(br->overlay_flags); + + return stroke; } void paint_stroke_data_free(struct wmOperator *op) { - BKE_paint_set_overlay_override(0); - MEM_SAFE_FREE(op->customdata); + BKE_paint_set_overlay_override(0); + MEM_SAFE_FREE(op->customdata); } static void stroke_done(struct bContext *C, struct wmOperator *op) { - struct PaintStroke *stroke = op->customdata; - UnifiedPaintSettings *ups = stroke->ups; + struct PaintStroke *stroke = op->customdata; + UnifiedPaintSettings *ups = stroke->ups; - ups->draw_anchored = false; - ups->stroke_active = false; + ups->draw_anchored = false; + ups->stroke_active = false; - /* reset rotation here to avoid doing so in cursor display */ - if (!(stroke->brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) - ups->brush_rotation = 0.0f; + /* reset rotation here to avoid doing so in cursor display */ + if (!(stroke->brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) + ups->brush_rotation = 0.0f; - if (!(stroke->brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) - ups->brush_rotation_sec = 0.0f; + if (!(stroke->brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) + ups->brush_rotation_sec = 0.0f; - if (stroke->stroke_started) { - if (stroke->redraw) - stroke->redraw(C, stroke, true); + if (stroke->stroke_started) { + if (stroke->redraw) + stroke->redraw(C, stroke, true); - if (stroke->done) - stroke->done(C, stroke); - } + if (stroke->done) + stroke->done(C, stroke); + } - if (stroke->timer) { - WM_event_remove_timer( - CTX_wm_manager(C), - CTX_wm_window(C), - stroke->timer); - } + if (stroke->timer) { + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), stroke->timer); + } - if (stroke->rng) { - BLI_rng_free(stroke->rng); - } + if (stroke->rng) { + BLI_rng_free(stroke->rng); + } - if (stroke->stroke_cursor) - WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); + if (stroke->stroke_cursor) + WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); - BLI_freelistN(&stroke->line); + BLI_freelistN(&stroke->line); - paint_stroke_data_free(op); + paint_stroke_data_free(op); } /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) { - return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br, mode); + return (br->flag & BRUSH_SPACE) && paint_supports_dynamic_size(br, mode); } static bool sculpt_is_grab_tool(Brush *br) { - return ELEM( - br->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_SNAKE_HOOK); + return ELEM(br->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SNAKE_HOOK); } /* return true if the brush size can change during paint (normally used for pressure) */ bool paint_supports_dynamic_size(Brush *br, ePaintMode mode) { - if (br->flag & BRUSH_ANCHORED) - return false; - - switch (mode) { - case PAINT_MODE_SCULPT: - if (sculpt_is_grab_tool(br)) - return false; - break; - - case PAINT_MODE_TEXTURE_2D: /* fall through */ - case PAINT_MODE_TEXTURE_3D: - if ((br->imagepaint_tool == PAINT_TOOL_FILL) && - (br->flag & BRUSH_USE_GRADIENT)) - { - return false; - } - break; - - default: - break; - } - return true; + if (br->flag & BRUSH_ANCHORED) + return false; + + switch (mode) { + case PAINT_MODE_SCULPT: + if (sculpt_is_grab_tool(br)) + return false; + break; + + case PAINT_MODE_TEXTURE_2D: /* fall through */ + case PAINT_MODE_TEXTURE_3D: + if ((br->imagepaint_tool == PAINT_TOOL_FILL) && (br->flag & BRUSH_USE_GRADIENT)) { + return false; + } + break; + + default: + break; + } + return true; } bool paint_supports_smooth_stroke(Brush *br, ePaintMode mode) { - if (!(br->flag & BRUSH_SMOOTH_STROKE) || - (br->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT | BRUSH_LINE))) - { - return false; - } - - switch (mode) { - case PAINT_MODE_SCULPT: - if (sculpt_is_grab_tool(br)) - return false; - break; - default: - break; - } - return true; + if (!(br->flag & BRUSH_SMOOTH_STROKE) || + (br->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT | BRUSH_LINE))) { + return false; + } + + switch (mode) { + case PAINT_MODE_SCULPT: + if (sculpt_is_grab_tool(br)) + return false; + break; + default: + break; + } + return true; } bool paint_supports_texture(ePaintMode mode) { - /* omit: PAINT_WEIGHT, PAINT_SCULPT_UV, PAINT_INVALID */ - return ELEM(mode, PAINT_MODE_SCULPT, PAINT_MODE_VERTEX, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D); + /* omit: PAINT_WEIGHT, PAINT_SCULPT_UV, PAINT_INVALID */ + return ELEM( + mode, PAINT_MODE_SCULPT, PAINT_MODE_VERTEX, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D); } /* return true if the brush size can change during paint (normally used for pressure) */ bool paint_supports_dynamic_tex_coords(Brush *br, ePaintMode mode) { - if (br->flag & BRUSH_ANCHORED) - return false; - - switch (mode) { - case PAINT_MODE_SCULPT: - if (sculpt_is_grab_tool(br)) - return false; - break; - default: - break; - } - return true; + if (br->flag & BRUSH_ANCHORED) + return false; + + switch (mode) { + case PAINT_MODE_SCULPT: + if (sculpt_is_grab_tool(br)) + return false; + break; + default: + break; + } + return true; } #define PAINT_STROKE_MODAL_CANCEL 1 @@ -915,487 +916,486 @@ bool paint_supports_dynamic_tex_coords(Brush *br, ePaintMode mode) /* called in paint_ops.c, on each regeneration of keymaps */ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf) { - static struct EnumPropertyItem modal_items[] = { - {PAINT_STROKE_MODAL_CANCEL, "CANCEL", 0, - "Cancel", - "Cancel and undo a stroke in progress"}, + static struct EnumPropertyItem modal_items[] = { + {PAINT_STROKE_MODAL_CANCEL, "CANCEL", 0, "Cancel", "Cancel and undo a stroke in progress"}, - { 0 } - }; + {0}}; - static const char *name = "Paint Stroke Modal"; + static const char *name = "Paint Stroke Modal"; - struct wmKeyMap *keymap = WM_modalkeymap_get(keyconf, name); + struct wmKeyMap *keymap = WM_modalkeymap_get(keyconf, name); - /* this function is called for each spacetype, only needs to add map once */ - if (!keymap) { - keymap = WM_modalkeymap_add(keyconf, name, modal_items); - } + /* this function is called for each spacetype, only needs to add map once */ + if (!keymap) { + keymap = WM_modalkeymap_add(keyconf, name, modal_items); + } - return keymap; + return keymap; } static void paint_stroke_add_sample( - const Paint *paint, - PaintStroke *stroke, - float x, float y, float pressure) + const Paint *paint, PaintStroke *stroke, float x, float y, float pressure) { - PaintSample *sample = &stroke->samples[stroke->cur_sample]; - int max_samples = CLAMPIS(paint->num_input_samples, 1, PAINT_MAX_INPUT_SAMPLES); - - sample->mouse[0] = x; - sample->mouse[1] = y; - sample->pressure = pressure; - - stroke->cur_sample++; - if (stroke->cur_sample >= max_samples) - stroke->cur_sample = 0; - if (stroke->num_samples < max_samples) - stroke->num_samples++; + PaintSample *sample = &stroke->samples[stroke->cur_sample]; + int max_samples = CLAMPIS(paint->num_input_samples, 1, PAINT_MAX_INPUT_SAMPLES); + + sample->mouse[0] = x; + sample->mouse[1] = y; + sample->pressure = pressure; + + stroke->cur_sample++; + if (stroke->cur_sample >= max_samples) + stroke->cur_sample = 0; + if (stroke->num_samples < max_samples) + stroke->num_samples++; } -static void paint_stroke_sample_average( - const PaintStroke *stroke, - PaintSample *average) +static void paint_stroke_sample_average(const PaintStroke *stroke, PaintSample *average) { - int i; + int i; - memset(average, 0, sizeof(*average)); + memset(average, 0, sizeof(*average)); - BLI_assert(stroke->num_samples > 0); + BLI_assert(stroke->num_samples > 0); - for (i = 0; i < stroke->num_samples; i++) { - add_v2_v2(average->mouse, stroke->samples[i].mouse); - average->pressure += stroke->samples[i].pressure; - } + for (i = 0; i < stroke->num_samples; i++) { + add_v2_v2(average->mouse, stroke->samples[i].mouse); + average->pressure += stroke->samples[i].pressure; + } - mul_v2_fl(average->mouse, 1.0f / stroke->num_samples); - average->pressure /= stroke->num_samples; + mul_v2_fl(average->mouse, 1.0f / stroke->num_samples); + average->pressure /= stroke->num_samples; - // printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples); + // printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples); } /** * Slightly different version of spacing for line/curve strokes, * makes sure the dabs stay on the line path. */ -static void paint_line_strokes_spacing( - bContext *C, wmOperator *op, PaintStroke *stroke, float spacing, float *length_residue, - const float old_pos[2], const float new_pos[2]) +static void paint_line_strokes_spacing(bContext *C, + wmOperator *op, + PaintStroke *stroke, + float spacing, + float *length_residue, + const float old_pos[2], + const float new_pos[2]) { - UnifiedPaintSettings *ups = stroke->ups; + UnifiedPaintSettings *ups = stroke->ups; - float mouse[2], dmouse[2]; - float length; + float mouse[2], dmouse[2]; + float length; - sub_v2_v2v2(dmouse, new_pos, old_pos); - copy_v2_v2(stroke->last_mouse_position, old_pos); + sub_v2_v2v2(dmouse, new_pos, old_pos); + copy_v2_v2(stroke->last_mouse_position, old_pos); - length = normalize_v2(dmouse); + length = normalize_v2(dmouse); - BLI_assert(length >= 0.0f); + BLI_assert(length >= 0.0f); - if (length == 0.0f) - return; + if (length == 0.0f) + return; - while (length > 0.0f) { - float spacing_final = spacing - *length_residue; - length += *length_residue; - *length_residue = 0.0; + while (length > 0.0f) { + float spacing_final = spacing - *length_residue; + length += *length_residue; + *length_residue = 0.0; - if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final; + if (length >= spacing) { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final; - ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); + ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); - stroke->stroke_distance += spacing / stroke->zoom_2d; - paint_brush_stroke_add_step(C, op, mouse, 1.0); + stroke->stroke_distance += spacing / stroke->zoom_2d; + paint_brush_stroke_add_step(C, op, mouse, 1.0); - length -= spacing; - spacing_final = spacing; - } - else { - break; - } - } + length -= spacing; + spacing_final = spacing; + } + else { + break; + } + } - *length_residue = length; + *length_residue = length; } - static void paint_stroke_line_end(bContext *C, wmOperator *op, PaintStroke *stroke, float mouse[2]) { - Brush *br = stroke->brush; - if (stroke->stroke_started && (br->flag & BRUSH_LINE)) { - stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + Brush *br = stroke->brush; + if (stroke->stroke_started && (br->flag & BRUSH_LINE)) { + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0); - paint_space_stroke(C, op, mouse, 1.0); - } + paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0); + paint_space_stroke(C, op, mouse, 1.0); + } } static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *stroke) { - Brush *br = stroke->brush; + Brush *br = stroke->brush; - if (br->flag & BRUSH_CURVE) { - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - const Scene *scene = CTX_data_scene(C); - const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); - PaintCurve *pc = br->paint_curve; - PaintCurvePoint *pcp; - float length_residue = 0.0f; - int i; + if (br->flag & BRUSH_CURVE) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + const Scene *scene = CTX_data_scene(C); + const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); + PaintCurve *pc = br->paint_curve; + PaintCurvePoint *pcp; + float length_residue = 0.0f; + int i; - if (!pc) - return true; + if (!pc) + return true; #ifdef DEBUG_TIME - TIMEIT_START_AVERAGED(whole_stroke); + TIMEIT_START_AVERAGED(whole_stroke); #endif - pcp = pc->points; - stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - - for (i = 0; i < pc->tot_points - 1; i++, pcp++) { - int j; - float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; - float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; - PaintCurvePoint *pcp_next = pcp + 1; - bool do_rake = false; - - for (j = 0; j < 2; j++) { - BKE_curve_forward_diff_bezier( - pcp->bez.vec[1][j], - pcp->bez.vec[2][j], - pcp_next->bez.vec[0][j], - pcp_next->bez.vec[1][j], - data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); - } - - if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || - (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) - { - do_rake = true; - for (j = 0; j < 2; j++) { - BKE_curve_forward_diff_tangent_bezier( - pcp->bez.vec[1][j], - pcp->bez.vec[2][j], - pcp_next->bez.vec[0][j], - pcp_next->bez.vec[1][j], - tangents + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); - } - } - - for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { - if (do_rake) { - float rotation = atan2f(tangents[2 * j], tangents[2 * j + 1]); - paint_update_brush_rake_rotation(ups, br, rotation); - } - - if (!stroke->stroke_started) { - stroke->last_pressure = 1.0; - copy_v2_v2(stroke->last_mouse_position, data + 2 * j); - stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); - - if (stroke->stroke_started) { - paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0); - paint_line_strokes_spacing( - C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); - } - } - else { - paint_line_strokes_spacing( - C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); - } - } - } - - stroke_done(C, op); + pcp = pc->points; + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + + for (i = 0; i < pc->tot_points - 1; i++, pcp++) { + int j; + float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + PaintCurvePoint *pcp_next = pcp + 1; + bool do_rake = false; + + for (j = 0; j < 2; j++) { + BKE_curve_forward_diff_bezier(pcp->bez.vec[1][j], + pcp->bez.vec[2][j], + pcp_next->bez.vec[0][j], + pcp_next->bez.vec[1][j], + data + j, + PAINT_CURVE_NUM_SEGMENTS, + sizeof(float[2])); + } + + if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || + (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { + do_rake = true; + for (j = 0; j < 2; j++) { + BKE_curve_forward_diff_tangent_bezier(pcp->bez.vec[1][j], + pcp->bez.vec[2][j], + pcp_next->bez.vec[0][j], + pcp_next->bez.vec[1][j], + tangents + j, + PAINT_CURVE_NUM_SEGMENTS, + sizeof(float[2])); + } + } + + for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { + if (do_rake) { + float rotation = atan2f(tangents[2 * j], tangents[2 * j + 1]); + paint_update_brush_rake_rotation(ups, br, rotation); + } + + if (!stroke->stroke_started) { + stroke->last_pressure = 1.0; + copy_v2_v2(stroke->last_mouse_position, data + 2 * j); + stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); + + if (stroke->stroke_started) { + paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0); + paint_line_strokes_spacing( + C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); + } + } + else { + paint_line_strokes_spacing( + C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); + } + } + } + + stroke_done(C, op); #ifdef DEBUG_TIME - TIMEIT_END_AVERAGED(whole_stroke); + TIMEIT_END_AVERAGED(whole_stroke); #endif - return true; - } + return true; + } - return false; + return false; } static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2]) { - if (stroke->constrain_line) { - float line[2]; - float angle, len, res; - - sub_v2_v2v2(line, mouse, stroke->last_mouse_position); - angle = atan2f(line[1], line[0]); - len = len_v2(line); - - /* divide angle by PI/4 */ - angle = 4.0f * angle / (float)M_PI; - - /* now take residue */ - res = angle - floorf(angle); - - /* residue decides how close we are at a certain angle */ - if (res <= 0.5f) { - angle = floorf(angle) * (float)M_PI_4; - } - else { - angle = (floorf(angle) + 1.0f) * (float)M_PI_4; - } - - mouse[0] = stroke->constrained_pos[0] = len * cosf(angle) + stroke->last_mouse_position[0]; - mouse[1] = stroke->constrained_pos[1] = len * sinf(angle) + stroke->last_mouse_position[1]; - } + if (stroke->constrain_line) { + float line[2]; + float angle, len, res; + + sub_v2_v2v2(line, mouse, stroke->last_mouse_position); + angle = atan2f(line[1], line[0]); + len = len_v2(line); + + /* divide angle by PI/4 */ + angle = 4.0f * angle / (float)M_PI; + + /* now take residue */ + res = angle - floorf(angle); + + /* residue decides how close we are at a certain angle */ + if (res <= 0.5f) { + angle = floorf(angle) * (float)M_PI_4; + } + else { + angle = (floorf(angle) + 1.0f) * (float)M_PI_4; + } + + mouse[0] = stroke->constrained_pos[0] = len * cosf(angle) + stroke->last_mouse_position[0]; + mouse[1] = stroke->constrained_pos[1] = len * sinf(angle) + stroke->last_mouse_position[1]; + } } int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Paint *p = BKE_paint_get_active_from_context(C); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - PaintStroke *stroke = op->customdata; - Brush *br = stroke->brush; - PaintSample sample_average; - float mouse[2]; - bool first_dab = false; - bool first_modal = false; - bool redraw = false; - float pressure; - - /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */ - pressure = ( - (br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? - 1.0f : WM_event_tablet_data(event, &stroke->pen_flip, NULL)); - - paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); - paint_stroke_sample_average(stroke, &sample_average); + Paint *p = BKE_paint_get_active_from_context(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + PaintStroke *stroke = op->customdata; + Brush *br = stroke->brush; + PaintSample sample_average; + float mouse[2]; + bool first_dab = false; + bool first_modal = false; + bool redraw = false; + float pressure; + + /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */ + pressure = ((br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? + 1.0f : + WM_event_tablet_data(event, &stroke->pen_flip, NULL)); + + paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); + paint_stroke_sample_average(stroke, &sample_average); #ifdef WITH_INPUT_NDOF - /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously! - * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it - * since the 2D deltas are zero -- code in this file needs to be updated to use the - * post-NDOF_MOTION MOUSEMOVE */ - if (event->type == NDOF_MOTION) - return OPERATOR_PASS_THROUGH; + /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously! + * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it + * since the 2D deltas are zero -- code in this file needs to be updated to use the + * post-NDOF_MOTION MOUSEMOVE */ + if (event->type == NDOF_MOTION) + return OPERATOR_PASS_THROUGH; #endif - /* one time initialization */ - if (!stroke->stroke_init) { - if (paint_stroke_curve_end(C, op, stroke)) - return OPERATOR_FINISHED; - - if (paint_supports_smooth_stroke(br, mode)) - stroke->stroke_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - paint_poll, paint_draw_smooth_cursor, stroke); - - stroke->stroke_init = true; - first_modal = true; - } - - /* one time stroke initialization */ - if (!stroke->stroke_started) { - stroke->last_pressure = sample_average.pressure; - copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); - stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); - BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ - - if (stroke->stroke_started) { - if (br->flag & BRUSH_AIRBRUSH) - stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); - - if (br->flag & BRUSH_LINE) { - stroke->stroke_cursor = WM_paint_cursor_activate( - CTX_wm_manager(C), - SPACE_TYPE_ANY, RGN_TYPE_ANY, - paint_poll, paint_draw_line_cursor, stroke); - } - - first_dab = true; - } - } - - /* Cancel */ - if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) { - if (op->type->cancel) { - op->type->cancel(C, op); - } - else { - paint_stroke_cancel(C, op); - } - return OPERATOR_CANCELLED; - } - - if (event->type == stroke->event_type && !first_modal) { - if (event->val == KM_RELEASE) { - copy_v2_fl2(mouse, event->mval[0], event->mval[1]); - paint_stroke_line_constrain(stroke, mouse); - paint_stroke_line_end(C, op, stroke, mouse); - stroke_done(C, op); - return OPERATOR_FINISHED; - } - } - else if (ELEM(event->type, RETKEY, SPACEKEY)) { - paint_stroke_line_end(C, op, stroke, sample_average.mouse); - stroke_done(C, op); - return OPERATOR_FINISHED; - } - else if (br->flag & BRUSH_LINE) { - if (event->alt) - stroke->constrain_line = true; - else - stroke->constrain_line = false; - - copy_v2_fl2(mouse, event->mval[0], event->mval[1]); - paint_stroke_line_constrain(stroke, mouse); - - if (stroke->stroke_started && (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) { - if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { - copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); - } - paint_calculate_rake_rotation(stroke->ups, br, mouse); - } - } - else if (first_modal || - /* regular dabs */ - (!(br->flag & (BRUSH_AIRBRUSH)) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || - /* airbrush */ - ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) - { - if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) { - if (stroke->stroke_started) { - if (paint_space_stroke_enabled(br, mode)) { - if (paint_space_stroke(C, op, mouse, pressure)) - redraw = true; - } - else { - float dmouse[2]; - sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); - stroke->stroke_distance += len_v2(dmouse); - paint_brush_stroke_add_step(C, op, mouse, pressure); - redraw = true; - } - } - } - } - - /* we want the stroke to have the first daub at the start location - * instead of waiting till we have moved the space distance */ - if (first_dab && - paint_space_stroke_enabled(br, mode) && - !(br->flag & BRUSH_SMOOTH_STROKE)) - { - stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure); - redraw = true; - } - - /* do updates for redraw. if event is inbetween mousemove there are more - * coming, so postpone potentially slow redraw updates until all are done */ - if (event->type != INBETWEEN_MOUSEMOVE) { - wmWindow *window = CTX_wm_window(C); - ARegion *ar = CTX_wm_region(C); - - /* At the very least, invalidate the cursor */ - if (ar && (p->flags & PAINT_SHOW_BRUSH)) - WM_paint_cursor_tag_redraw(window, ar); - - if (redraw && stroke->redraw) - stroke->redraw(C, stroke, false); - } - - return OPERATOR_RUNNING_MODAL; + /* one time initialization */ + if (!stroke->stroke_init) { + if (paint_stroke_curve_end(C, op, stroke)) + return OPERATOR_FINISHED; + + if (paint_supports_smooth_stroke(br, mode)) + stroke->stroke_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + paint_poll, + paint_draw_smooth_cursor, + stroke); + + stroke->stroke_init = true; + first_modal = true; + } + + /* one time stroke initialization */ + if (!stroke->stroke_started) { + stroke->last_pressure = sample_average.pressure; + copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); + stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); + BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ + + if (stroke->stroke_started) { + if (br->flag & BRUSH_AIRBRUSH) + stroke->timer = WM_event_add_timer( + CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); + + if (br->flag & BRUSH_LINE) { + stroke->stroke_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), + SPACE_TYPE_ANY, + RGN_TYPE_ANY, + paint_poll, + paint_draw_line_cursor, + stroke); + } + + first_dab = true; + } + } + + /* Cancel */ + if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) { + if (op->type->cancel) { + op->type->cancel(C, op); + } + else { + paint_stroke_cancel(C, op); + } + return OPERATOR_CANCELLED; + } + + if (event->type == stroke->event_type && !first_modal) { + if (event->val == KM_RELEASE) { + copy_v2_fl2(mouse, event->mval[0], event->mval[1]); + paint_stroke_line_constrain(stroke, mouse); + paint_stroke_line_end(C, op, stroke, mouse); + stroke_done(C, op); + return OPERATOR_FINISHED; + } + } + else if (ELEM(event->type, RETKEY, SPACEKEY)) { + paint_stroke_line_end(C, op, stroke, sample_average.mouse); + stroke_done(C, op); + return OPERATOR_FINISHED; + } + else if (br->flag & BRUSH_LINE) { + if (event->alt) + stroke->constrain_line = true; + else + stroke->constrain_line = false; + + copy_v2_fl2(mouse, event->mval[0], event->mval[1]); + paint_stroke_line_constrain(stroke, mouse); + + if (stroke->stroke_started && + (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) { + if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || + (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { + copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); + } + paint_calculate_rake_rotation(stroke->ups, br, mouse); + } + } + else if (first_modal || + /* regular dabs */ + (!(br->flag & (BRUSH_AIRBRUSH)) && + (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || + /* airbrush */ + ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && + event->customdata == stroke->timer)) { + if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) { + if (stroke->stroke_started) { + if (paint_space_stroke_enabled(br, mode)) { + if (paint_space_stroke(C, op, mouse, pressure)) + redraw = true; + } + else { + float dmouse[2]; + sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); + stroke->stroke_distance += len_v2(dmouse); + paint_brush_stroke_add_step(C, op, mouse, pressure); + redraw = true; + } + } + } + } + + /* we want the stroke to have the first daub at the start location + * instead of waiting till we have moved the space distance */ + if (first_dab && paint_space_stroke_enabled(br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) { + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure); + redraw = true; + } + + /* do updates for redraw. if event is inbetween mousemove there are more + * coming, so postpone potentially slow redraw updates until all are done */ + if (event->type != INBETWEEN_MOUSEMOVE) { + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + + /* At the very least, invalidate the cursor */ + if (ar && (p->flags & PAINT_SHOW_BRUSH)) + WM_paint_cursor_tag_redraw(window, ar); + + if (redraw && stroke->redraw) + stroke->redraw(C, stroke, false); + } + + return OPERATOR_RUNNING_MODAL; } int paint_stroke_exec(bContext *C, wmOperator *op) { - PaintStroke *stroke = op->customdata; + PaintStroke *stroke = op->customdata; - /* only when executed for the first time */ - if (stroke->stroke_started == 0) { - PropertyRNA *strokeprop; - PointerRNA firstpoint; - float mouse[2]; + /* only when executed for the first time */ + if (stroke->stroke_started == 0) { + PropertyRNA *strokeprop; + PointerRNA firstpoint; + float mouse[2]; - strokeprop = RNA_struct_find_property(op->ptr, "stroke"); + strokeprop = RNA_struct_find_property(op->ptr, "stroke"); - if (RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) { - RNA_float_get_array(&firstpoint, "mouse", mouse); - stroke->stroke_started = stroke->test_start(C, op, mouse); - } - } + if (RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) { + RNA_float_get_array(&firstpoint, "mouse", mouse); + stroke->stroke_started = stroke->test_start(C, op, mouse); + } + } - if (stroke->stroke_started) { - RNA_BEGIN (op->ptr, itemptr, "stroke") - { - stroke->update_step(C, stroke, &itemptr); - } - RNA_END; - } + if (stroke->stroke_started) { + RNA_BEGIN (op->ptr, itemptr, "stroke") { + stroke->update_step(C, stroke, &itemptr); + } + RNA_END; + } - bool ok = (stroke->stroke_started != 0); + bool ok = (stroke->stroke_started != 0); - stroke_done(C, op); + stroke_done(C, op); - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void paint_stroke_cancel(bContext *C, wmOperator *op) { - stroke_done(C, op); + stroke_done(C, op); } ViewContext *paint_stroke_view_context(PaintStroke *stroke) { - return &stroke->vc; + return &stroke->vc; } void *paint_stroke_mode_data(struct PaintStroke *stroke) { - return stroke->mode_data; + return stroke->mode_data; } bool paint_stroke_flipped(struct PaintStroke *stroke) { - return stroke->pen_flip; + return stroke->pen_flip; } bool paint_stroke_inverted(struct PaintStroke *stroke) { - return stroke->stroke_mode == BRUSH_STROKE_INVERT; + return stroke->stroke_mode == BRUSH_STROKE_INVERT; } float paint_stroke_distance_get(struct PaintStroke *stroke) { - return stroke->stroke_distance; + return stroke->stroke_distance; } void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) { - stroke->mode_data = mode_data; + stroke->mode_data = mode_data; } bool paint_poll(bContext *C) { - Paint *p = BKE_paint_get_active_from_context(C); - Object *ob = CTX_data_active_object(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - - if (p && ob && BKE_paint_brush(p) && - (sa && ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) && - (ar && ar->regiontype == RGN_TYPE_WINDOW)) - { - /* Check the current tool is a brush. */ - bToolRef *tref = sa->runtime.tool; - if (tref && tref->runtime && tref->runtime->data_block[0]) { - return true; - } - } - return false; + Paint *p = BKE_paint_get_active_from_context(C); + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + + if (p && ob && BKE_paint_brush(p) && (sa && ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) && + (ar && ar->regiontype == RGN_TYPE_WINDOW)) { + /* Check the current tool is a brush. */ + bToolRef *tref = sa->runtime.tool; + if (tref && tref->runtime && tref->runtime->data_block[0]) { + return true; + } + } + return false; } diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 35401c470cc..098e38dbdb5 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -80,699 +80,720 @@ /* Convert the object-space axis-aligned bounding box (expressed as * its minimum and maximum corners) into a screen-space rectangle, * returns zero if the result is empty */ -bool paint_convert_bb_to_rect( - rcti *rect, - const float bb_min[3], - const float bb_max[3], - const ARegion *ar, - RegionView3D *rv3d, - Object *ob) +bool paint_convert_bb_to_rect(rcti *rect, + const float bb_min[3], + const float bb_max[3], + const ARegion *ar, + RegionView3D *rv3d, + Object *ob) { - float projection_mat[4][4]; - int i, j, k; - - BLI_rcti_init_minmax(rect); - - /* return zero if the bounding box has non-positive volume */ - if (bb_min[0] > bb_max[0] || bb_min[1] > bb_max[1] || bb_min[2] > bb_max[2]) - return 0; - - ED_view3d_ob_project_mat_get(rv3d, ob, projection_mat); - - for (i = 0; i < 2; ++i) { - for (j = 0; j < 2; ++j) { - for (k = 0; k < 2; ++k) { - float vec[3], proj[2]; - int proj_i[2]; - vec[0] = i ? bb_min[0] : bb_max[0]; - vec[1] = j ? bb_min[1] : bb_max[1]; - vec[2] = k ? bb_min[2] : bb_max[2]; - /* convert corner to screen space */ - ED_view3d_project_float_v2_m4(ar, vec, proj, projection_mat); - /* expand 2D rectangle */ - - /* we could project directly to int? */ - proj_i[0] = proj[0]; - proj_i[1] = proj[1]; - - BLI_rcti_do_minmax_v(rect, proj_i); - } - } - } - - /* return false if the rectangle has non-positive area */ - return rect->xmin < rect->xmax && rect->ymin < rect->ymax; + float projection_mat[4][4]; + int i, j, k; + + BLI_rcti_init_minmax(rect); + + /* return zero if the bounding box has non-positive volume */ + if (bb_min[0] > bb_max[0] || bb_min[1] > bb_max[1] || bb_min[2] > bb_max[2]) + return 0; + + ED_view3d_ob_project_mat_get(rv3d, ob, projection_mat); + + for (i = 0; i < 2; ++i) { + for (j = 0; j < 2; ++j) { + for (k = 0; k < 2; ++k) { + float vec[3], proj[2]; + int proj_i[2]; + vec[0] = i ? bb_min[0] : bb_max[0]; + vec[1] = j ? bb_min[1] : bb_max[1]; + vec[2] = k ? bb_min[2] : bb_max[2]; + /* convert corner to screen space */ + ED_view3d_project_float_v2_m4(ar, vec, proj, projection_mat); + /* expand 2D rectangle */ + + /* we could project directly to int? */ + proj_i[0] = proj[0]; + proj_i[1] = proj[1]; + + BLI_rcti_do_minmax_v(rect, proj_i); + } + } + } + + /* return false if the rectangle has non-positive area */ + return rect->xmin < rect->xmax && rect->ymin < rect->ymax; } /* Get four planes in object-space that describe the projection of * screen_rect from screen into object-space (essentially converting a * 2D screens-space bounding box into four 3D planes) */ -void paint_calc_redraw_planes( - float planes[4][4], - const ARegion *ar, - Object *ob, - const rcti *screen_rect) +void paint_calc_redraw_planes(float planes[4][4], + const ARegion *ar, + Object *ob, + const rcti *screen_rect) { - BoundBox bb; - rcti rect; - - /* use some extra space just in case */ - rect = *screen_rect; - rect.xmin -= 2; - rect.xmax += 2; - rect.ymin -= 2; - rect.ymax += 2; - - ED_view3d_clipping_calc(&bb, planes, ar, ob, &rect); - negate_m4(planes); + BoundBox bb; + rcti rect; + + /* use some extra space just in case */ + rect = *screen_rect; + rect.xmin -= 2; + rect.xmax += 2; + rect.ymin -= 2; + rect.ymax += 2; + + ED_view3d_clipping_calc(&bb, planes, ar, ob, &rect); + negate_m4(planes); } -float paint_calc_object_space_radius( - ViewContext *vc, const float center[3], - float pixel_radius) +float paint_calc_object_space_radius(ViewContext *vc, const float center[3], float pixel_radius) { - Object *ob = vc->obact; - float delta[3], scale, loc[3]; - const float mval_f[2] = {pixel_radius, 0.0f}; - float zfac; + Object *ob = vc->obact; + float delta[3], scale, loc[3]; + const float mval_f[2] = {pixel_radius, 0.0f}; + float zfac; - mul_v3_m4v3(loc, ob->obmat, center); + mul_v3_m4v3(loc, ob->obmat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); - ED_view3d_win_to_delta(vc->ar, mval_f, delta, zfac); + zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); + ED_view3d_win_to_delta(vc->ar, mval_f, delta, zfac); - scale = fabsf(mat4_to_scale(ob->obmat)); - scale = (scale == 0.0f) ? 1.0f : scale; + scale = fabsf(mat4_to_scale(ob->obmat)); + scale = (scale == 0.0f) ? 1.0f : scale; - return len_v3(delta) / scale; + return len_v3(delta) / scale; } float paint_get_tex_pixel(const MTex *mtex, float u, float v, struct ImagePool *pool, int thread) { - float intensity, rgba[4]; - float co[3] = {u, v, 0.0f}; + float intensity, rgba[4]; + float co[3] = {u, v, 0.0f}; - externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); - return intensity; + return intensity; } -void paint_get_tex_pixel_col( - const MTex *mtex, float u, float v, float rgba[4], struct ImagePool *pool, - int thread, bool convert_to_linear, struct ColorSpace *colorspace) +void paint_get_tex_pixel_col(const MTex *mtex, + float u, + float v, + float rgba[4], + struct ImagePool *pool, + int thread, + bool convert_to_linear, + struct ColorSpace *colorspace) { - float co[3] = {u, v, 0.0f}; - int hasrgb; - float intensity; - - hasrgb = externtex(mtex, co, &intensity, - rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); - if (!hasrgb) { - rgba[0] = intensity; - rgba[1] = intensity; - rgba[2] = intensity; - rgba[3] = 1.0f; - } - - if (convert_to_linear) - IMB_colormanagement_colorspace_to_scene_linear_v3(rgba, colorspace); - - linearrgb_to_srgb_v3_v3(rgba, rgba); - - CLAMP(rgba[0], 0.0f, 1.0f); - CLAMP(rgba[1], 0.0f, 1.0f); - CLAMP(rgba[2], 0.0f, 1.0f); - CLAMP(rgba[3], 0.0f, 1.0f); + float co[3] = {u, v, 0.0f}; + int hasrgb; + float intensity; + + hasrgb = externtex( + mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + if (!hasrgb) { + rgba[0] = intensity; + rgba[1] = intensity; + rgba[2] = intensity; + rgba[3] = 1.0f; + } + + if (convert_to_linear) + IMB_colormanagement_colorspace_to_scene_linear_v3(rgba, colorspace); + + linearrgb_to_srgb_v3_v3(rgba, rgba); + + CLAMP(rgba[0], 0.0f, 1.0f); + CLAMP(rgba[1], 0.0f, 1.0f); + CLAMP(rgba[2], 0.0f, 1.0f); + CLAMP(rgba[3], 0.0f, 1.0f); } void paint_stroke_operator_properties(wmOperatorType *ot) { - static const EnumPropertyItem stroke_mode_items[] = { - {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"}, - {BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"}, - {BRUSH_STROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke"}, - {0}, - }; - - PropertyRNA *prop; - - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - - RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, - "Stroke Mode", - "Action taken when a paint stroke is made"); - + static const EnumPropertyItem stroke_mode_items[] = { + {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"}, + {BRUSH_STROKE_INVERT, + "INVERT", + 0, + "Invert", + "Invert action of brush for duration of stroke"}, + {BRUSH_STROKE_SMOOTH, + "SMOOTH", + 0, + "Smooth", + "Switch brush to smooth mode for duration of stroke"}, + {0}, + }; + + PropertyRNA *prop; + + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + RNA_def_enum(ot->srna, + "mode", + stroke_mode_items, + BRUSH_STROKE_NORMAL, + "Stroke Mode", + "Action taken when a paint stroke is made"); } /* 3D Paint */ static void imapaint_project(float matrix[4][4], const float co[3], float pco[4]) { - copy_v3_v3(pco, co); - pco[3] = 1.0f; + copy_v3_v3(pco, co); + pco[3] = 1.0f; - mul_m4_v4(matrix, pco); + mul_m4_v4(matrix, pco); } -static void imapaint_tri_weights( - float matrix[4][4], GLint view[4], - const float v1[3], const float v2[3], const float v3[3], - const float co[2], float w[3]) +static void imapaint_tri_weights(float matrix[4][4], + GLint view[4], + const float v1[3], + const float v2[3], + const float v3[3], + const float co[2], + float w[3]) { - float pv1[4], pv2[4], pv3[4], h[3], divw; - float wmat[3][3], invwmat[3][3]; - - /* compute barycentric coordinates */ - - /* project the verts */ - imapaint_project(matrix, v1, pv1); - imapaint_project(matrix, v2, pv2); - imapaint_project(matrix, v3, pv3); - - /* do inverse view mapping, see gluProject man page */ - h[0] = (co[0] - view[0]) * 2.0f / view[2] - 1.0f; - h[1] = (co[1] - view[1]) * 2.0f / view[3] - 1.0f; - h[2] = 1.0f; - - /* solve for (w1,w2,w3)/perspdiv in: - * h * perspdiv = Project * Model * (w1 * v1 + w2 * v2 + w3 * v3) */ - - wmat[0][0] = pv1[0]; wmat[1][0] = pv2[0]; wmat[2][0] = pv3[0]; - wmat[0][1] = pv1[1]; wmat[1][1] = pv2[1]; wmat[2][1] = pv3[1]; - wmat[0][2] = pv1[3]; wmat[1][2] = pv2[3]; wmat[2][2] = pv3[3]; - - invert_m3_m3(invwmat, wmat); - mul_m3_v3(invwmat, h); - - copy_v3_v3(w, h); - - /* w is still divided by perspdiv, make it sum to one */ - divw = w[0] + w[1] + w[2]; - if (divw != 0.0f) { - mul_v3_fl(w, 1.0f / divw); - } + float pv1[4], pv2[4], pv3[4], h[3], divw; + float wmat[3][3], invwmat[3][3]; + + /* compute barycentric coordinates */ + + /* project the verts */ + imapaint_project(matrix, v1, pv1); + imapaint_project(matrix, v2, pv2); + imapaint_project(matrix, v3, pv3); + + /* do inverse view mapping, see gluProject man page */ + h[0] = (co[0] - view[0]) * 2.0f / view[2] - 1.0f; + h[1] = (co[1] - view[1]) * 2.0f / view[3] - 1.0f; + h[2] = 1.0f; + + /* solve for (w1,w2,w3)/perspdiv in: + * h * perspdiv = Project * Model * (w1 * v1 + w2 * v2 + w3 * v3) */ + + wmat[0][0] = pv1[0]; + wmat[1][0] = pv2[0]; + wmat[2][0] = pv3[0]; + wmat[0][1] = pv1[1]; + wmat[1][1] = pv2[1]; + wmat[2][1] = pv3[1]; + wmat[0][2] = pv1[3]; + wmat[1][2] = pv2[3]; + wmat[2][2] = pv3[3]; + + invert_m3_m3(invwmat, wmat); + mul_m3_v3(invwmat, h); + + copy_v3_v3(w, h); + + /* w is still divided by perspdiv, make it sum to one */ + divw = w[0] + w[1] + w[2]; + if (divw != 0.0f) { + mul_v3_fl(w, 1.0f / divw); + } } /* compute uv coordinates of mouse in face */ -static void imapaint_pick_uv(Mesh *me_eval, Scene *scene, Object *ob_eval, unsigned int faceindex, const int xy[2], float uv[2]) +static void imapaint_pick_uv(Mesh *me_eval, + Scene *scene, + Object *ob_eval, + unsigned int faceindex, + const int xy[2], + float uv[2]) { - int i, findex; - float p[2], w[3], absw, minabsw; - float matrix[4][4], proj[4][4]; - GLint view[4]; - const eImagePaintMode mode = scene->toolsettings->imapaint.mode; - - const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval); - const int tottri = me_eval->runtime.looptris.len; - - const MVert *mvert = me_eval->mvert; - const MPoly *mpoly = me_eval->mpoly; - const MLoop *mloop = me_eval->mloop; - const int *index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); - - /* get the needed opengl matrices */ - GPU_viewport_size_get_i(view); - GPU_matrix_model_view_get(matrix); - GPU_matrix_projection_get(proj); - view[0] = view[1] = 0; - mul_m4_m4m4(matrix, matrix, ob_eval->obmat); - mul_m4_m4m4(matrix, proj, matrix); - - minabsw = 1e10; - uv[0] = uv[1] = 0.0; - - /* test all faces in the derivedmesh with the original index of the picked face */ - /* face means poly here, not triangle, indeed */ - for (i = 0; i < tottri; i++, lt++) { - findex = index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; - - if (findex == faceindex) { - const MLoopUV *mloopuv; - const MPoly *mp = &mpoly[lt->poly]; - const MLoopUV *tri_uv[3]; - float tri_co[3][3]; - - for (int j = 3; j--; ) { - copy_v3_v3(tri_co[j], mvert[mloop[lt->tri[j]].v].co); - } - - if (mode == IMAGEPAINT_MODE_MATERIAL) { - const Material *ma; - const TexPaintSlot *slot; - - ma = give_current_material(ob_eval, mp->mat_nr + 1); - slot = &ma->texpaintslot[ma->paint_active_slot]; - - if (!(slot && slot->uvname && - (mloopuv = CustomData_get_layer_named(&me_eval->ldata, CD_MLOOPUV, slot->uvname)))) - { - mloopuv = CustomData_get_layer(&me_eval->ldata, CD_MLOOPUV); - } - } - else { - mloopuv = CustomData_get_layer(&me_eval->ldata, CD_MLOOPUV); - } - - tri_uv[0] = &mloopuv[lt->tri[0]]; - tri_uv[1] = &mloopuv[lt->tri[1]]; - tri_uv[2] = &mloopuv[lt->tri[2]]; - - p[0] = xy[0]; - p[1] = xy[1]; - - imapaint_tri_weights(matrix, view, UNPACK3(tri_co), p, w); - absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); - if (absw < minabsw) { - uv[0] = tri_uv[0]->uv[0] * w[0] + tri_uv[1]->uv[0] * w[1] + tri_uv[2]->uv[0] * w[2]; - uv[1] = tri_uv[0]->uv[1] * w[0] + tri_uv[1]->uv[1] * w[1] + tri_uv[2]->uv[1] * w[2]; - minabsw = absw; - } - } - } + int i, findex; + float p[2], w[3], absw, minabsw; + float matrix[4][4], proj[4][4]; + GLint view[4]; + const eImagePaintMode mode = scene->toolsettings->imapaint.mode; + + const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval); + const int tottri = me_eval->runtime.looptris.len; + + const MVert *mvert = me_eval->mvert; + const MPoly *mpoly = me_eval->mpoly; + const MLoop *mloop = me_eval->mloop; + const int *index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); + + /* get the needed opengl matrices */ + GPU_viewport_size_get_i(view); + GPU_matrix_model_view_get(matrix); + GPU_matrix_projection_get(proj); + view[0] = view[1] = 0; + mul_m4_m4m4(matrix, matrix, ob_eval->obmat); + mul_m4_m4m4(matrix, proj, matrix); + + minabsw = 1e10; + uv[0] = uv[1] = 0.0; + + /* test all faces in the derivedmesh with the original index of the picked face */ + /* face means poly here, not triangle, indeed */ + for (i = 0; i < tottri; i++, lt++) { + findex = index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; + + if (findex == faceindex) { + const MLoopUV *mloopuv; + const MPoly *mp = &mpoly[lt->poly]; + const MLoopUV *tri_uv[3]; + float tri_co[3][3]; + + for (int j = 3; j--;) { + copy_v3_v3(tri_co[j], mvert[mloop[lt->tri[j]].v].co); + } + + if (mode == IMAGEPAINT_MODE_MATERIAL) { + const Material *ma; + const TexPaintSlot *slot; + + ma = give_current_material(ob_eval, mp->mat_nr + 1); + slot = &ma->texpaintslot[ma->paint_active_slot]; + + if (!(slot && slot->uvname && + (mloopuv = CustomData_get_layer_named(&me_eval->ldata, CD_MLOOPUV, slot->uvname)))) { + mloopuv = CustomData_get_layer(&me_eval->ldata, CD_MLOOPUV); + } + } + else { + mloopuv = CustomData_get_layer(&me_eval->ldata, CD_MLOOPUV); + } + + tri_uv[0] = &mloopuv[lt->tri[0]]; + tri_uv[1] = &mloopuv[lt->tri[1]]; + tri_uv[2] = &mloopuv[lt->tri[2]]; + + p[0] = xy[0]; + p[1] = xy[1]; + + imapaint_tri_weights(matrix, view, UNPACK3(tri_co), p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tri_uv[0]->uv[0] * w[0] + tri_uv[1]->uv[0] * w[1] + tri_uv[2]->uv[0] * w[2]; + uv[1] = tri_uv[0]->uv[1] * w[0] + tri_uv[1]->uv[1] * w[1] + tri_uv[2]->uv[1] * w[2]; + minabsw = absw; + } + } + } } /* returns 0 if not found, otherwise 1 */ -static int imapaint_pick_face( - ViewContext *vc, const int mval[2], - unsigned int *r_index, unsigned int totpoly) +static int imapaint_pick_face(ViewContext *vc, + const int mval[2], + unsigned int *r_index, + unsigned int totpoly) { - if (totpoly == 0) - return 0; + if (totpoly == 0) + return 0; - /* sample only on the exact position */ - *r_index = ED_view3d_select_id_sample(vc, mval[0], mval[1]); + /* sample only on the exact position */ + *r_index = ED_view3d_select_id_sample(vc, mval[0], mval[1]); - if ((*r_index) == 0 || (*r_index) > (unsigned int)totpoly) { - return 0; - } + if ((*r_index) == 0 || (*r_index) > (unsigned int)totpoly) { + return 0; + } - (*r_index)--; + (*r_index)--; - return 1; + return 1; } - static Image *imapaint_face_image(Object *ob, Mesh *me, int face_index) { - Image *ima; - MPoly *mp = me->mpoly + face_index; - Material *ma = give_current_material(ob, mp->mat_nr + 1); - ima = ma && ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; + Image *ima; + MPoly *mp = me->mpoly + face_index; + Material *ma = give_current_material(ob, mp->mat_nr + 1); + ima = ma && ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; - return ima; + return ima; } /* Uses symm to selectively flip any axis of a coordinate. */ void flip_v3_v3(float out[3], const float in[3], const char symm) { - if (symm & PAINT_SYMM_X) - out[0] = -in[0]; - else - out[0] = in[0]; - if (symm & PAINT_SYMM_Y) - out[1] = -in[1]; - else - out[1] = in[1]; - if (symm & PAINT_SYMM_Z) - out[2] = -in[2]; - else - out[2] = in[2]; + if (symm & PAINT_SYMM_X) + out[0] = -in[0]; + else + out[0] = in[0]; + if (symm & PAINT_SYMM_Y) + out[1] = -in[1]; + else + out[1] = in[1]; + if (symm & PAINT_SYMM_Z) + out[2] = -in[2]; + else + out[2] = in[2]; } void flip_qt_qt(float out[4], const float in[4], const char symm) { - float axis[3], angle; - - quat_to_axis_angle(axis, &angle, in); - normalize_v3(axis); - - if (symm & PAINT_SYMM_X) { - axis[0] *= -1.0f; - angle *= -1.0f; - } - if (symm & PAINT_SYMM_Y) { - axis[1] *= -1.0f; - angle *= -1.0f; - } - if (symm & PAINT_SYMM_Z) { - axis[2] *= -1.0f; - angle *= -1.0f; - } - - axis_angle_normalized_to_quat(out, axis, angle); + float axis[3], angle; + + quat_to_axis_angle(axis, &angle, in); + normalize_v3(axis); + + if (symm & PAINT_SYMM_X) { + axis[0] *= -1.0f; + angle *= -1.0f; + } + if (symm & PAINT_SYMM_Y) { + axis[1] *= -1.0f; + angle *= -1.0f; + } + if (symm & PAINT_SYMM_Z) { + axis[2] *= -1.0f; + angle *= -1.0f; + } + + axis_angle_normalized_to_quat(out, axis, angle); } /* used for both 3d view and image window */ -void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette) +void paint_sample_color( + bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette) { - Scene *scene = CTX_data_scene(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Palette *palette = BKE_paint_palette(paint); - PaletteColor *color = NULL; - Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - unsigned int col; - const unsigned char *cp; - - CLAMP(x, 0, ar->winx); - CLAMP(y, 0, ar->winy); - - if (use_palette) { - if (!palette) { - palette = BKE_palette_add(CTX_data_main(C), "Palette"); - BKE_paint_palette_set(paint, palette); - } - - color = BKE_palette_color_add(palette); - palette->active_color = BLI_listbase_count(&palette->colors) - 1; - } - - - if (CTX_wm_view3d(C) && texpaint_proj) { - /* first try getting a colour directly from the mesh faces if possible */ - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - bool sample_success = false; - ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; - bool use_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL); - - if (ob) { - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.pmask |= CD_MASK_ORIGINDEX; - Mesh *me = (Mesh *)ob->data; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob_eval, &cddata_masks); - - ViewContext vc; - const int mval[2] = {x, y}; - unsigned int faceindex; - unsigned int totpoly = me->totpoly; - - if (CustomData_has_layer(&me_eval->ldata, CD_MLOOPUV)) { - ED_view3d_viewcontext_init(C, &vc); - - view3d_operator_needs_opengl(C); - - if (imapaint_pick_face(&vc, mval, &faceindex, totpoly)) { - Image *image; - - if (use_material) - image = imapaint_face_image(ob_eval, me_eval, faceindex); - else - image = imapaint->canvas; - - if (image) { - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - if (ibuf && ibuf->rect) { - float uv[2]; - float u, v; - imapaint_pick_uv(me_eval, scene, ob_eval, faceindex, mval, uv); - sample_success = true; - - u = fmodf(uv[0], 1.0f); - v = fmodf(uv[1], 1.0f); - - if (u < 0.0f) u += 1.0f; - if (v < 0.0f) v += 1.0f; - - u = u * ibuf->x; - v = v * ibuf->y; - - if (ibuf->rect_float) { - float rgba_f[4]; - bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); - straight_to_premul_v4(rgba_f); - if (use_palette) { - linearrgb_to_srgb_v3_v3(color->rgb, rgba_f); - } - else { - linearrgb_to_srgb_v3_v3(rgba_f, rgba_f); - BKE_brush_color_set(scene, br, rgba_f); - } - } - else { - unsigned char rgba[4]; - bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); - if (use_palette) { - rgb_uchar_to_float(color->rgb, rgba); - } - else { - float rgba_f[3]; - rgb_uchar_to_float(rgba_f, rgba); - BKE_brush_color_set(scene, br, rgba_f); - } - } - } - - BKE_image_release_ibuf(image, ibuf, NULL); - } - } - } - } - - if (!sample_success) { - glReadBuffer(GL_FRONT); - glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); - glReadBuffer(GL_BACK); - } - else - return; - } - else { - glReadBuffer(GL_FRONT); - glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); - glReadBuffer(GL_BACK); - } - cp = (unsigned char *)&col; - - if (use_palette) { - rgb_uchar_to_float(color->rgb, cp); - } - else { - float rgba_f[3]; - rgb_uchar_to_float(rgba_f, cp); - BKE_brush_color_set(scene, br, rgba_f); - } + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = BKE_paint_palette(paint); + PaletteColor *color = NULL; + Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + unsigned int col; + const unsigned char *cp; + + CLAMP(x, 0, ar->winx); + CLAMP(y, 0, ar->winy); + + if (use_palette) { + if (!palette) { + palette = BKE_palette_add(CTX_data_main(C), "Palette"); + BKE_paint_palette_set(paint, palette); + } + + color = BKE_palette_color_add(palette); + palette->active_color = BLI_listbase_count(&palette->colors) - 1; + } + + if (CTX_wm_view3d(C) && texpaint_proj) { + /* first try getting a colour directly from the mesh faces if possible */ + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + bool sample_success = false; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + bool use_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL); + + if (ob) { + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; + cddata_masks.pmask |= CD_MASK_ORIGINDEX; + Mesh *me = (Mesh *)ob->data; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob_eval, &cddata_masks); + + ViewContext vc; + const int mval[2] = {x, y}; + unsigned int faceindex; + unsigned int totpoly = me->totpoly; + + if (CustomData_has_layer(&me_eval->ldata, CD_MLOOPUV)) { + ED_view3d_viewcontext_init(C, &vc); + + view3d_operator_needs_opengl(C); + + if (imapaint_pick_face(&vc, mval, &faceindex, totpoly)) { + Image *image; + + if (use_material) + image = imapaint_face_image(ob_eval, me_eval, faceindex); + else + image = imapaint->canvas; + + if (image) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (ibuf && ibuf->rect) { + float uv[2]; + float u, v; + imapaint_pick_uv(me_eval, scene, ob_eval, faceindex, mval, uv); + sample_success = true; + + u = fmodf(uv[0], 1.0f); + v = fmodf(uv[1], 1.0f); + + if (u < 0.0f) + u += 1.0f; + if (v < 0.0f) + v += 1.0f; + + u = u * ibuf->x; + v = v * ibuf->y; + + if (ibuf->rect_float) { + float rgba_f[4]; + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + straight_to_premul_v4(rgba_f); + if (use_palette) { + linearrgb_to_srgb_v3_v3(color->rgb, rgba_f); + } + else { + linearrgb_to_srgb_v3_v3(rgba_f, rgba_f); + BKE_brush_color_set(scene, br, rgba_f); + } + } + else { + unsigned char rgba[4]; + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + if (use_palette) { + rgb_uchar_to_float(color->rgb, rgba); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, rgba); + BKE_brush_color_set(scene, br, rgba_f); + } + } + } + + BKE_image_release_ibuf(image, ibuf, NULL); + } + } + } + } + + if (!sample_success) { + glReadBuffer(GL_FRONT); + glReadPixels( + x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + else + return; + } + else { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + cp = (unsigned char *)&col; + + if (use_palette) { + rgb_uchar_to_float(color->rgb, cp); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, cp); + BKE_brush_color_set(scene, br, rgba_f); + } } static int brush_curve_preset_exec(bContext *C, wmOperator *op) { - Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - if (br) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - BKE_brush_curve_preset(br, RNA_enum_get(op->ptr, "shape")); - BKE_paint_invalidate_cursor_overlay(scene, view_layer, br->curve); - } + if (br) { + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_brush_curve_preset(br, RNA_enum_get(op->ptr, "shape")); + BKE_paint_invalidate_cursor_overlay(scene, view_layer, br->curve); + } - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static bool brush_curve_preset_poll(bContext *C) { - Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - return br && br->curve; + return br && br->curve; } void BRUSH_OT_curve_preset(wmOperatorType *ot) { - PropertyRNA *prop; - static const EnumPropertyItem prop_shape_items[] = { - {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""}, - {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""}, - {CURVE_PRESET_MAX, "MAX", 0, "Max", ""}, - {CURVE_PRESET_LINE, "LINE", 0, "Line", ""}, - {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, - {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - ot->name = "Preset"; - ot->description = "Set brush shape"; - ot->idname = "BRUSH_OT_curve_preset"; - - ot->exec = brush_curve_preset_exec; - ot->poll = brush_curve_preset_poll; - - prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + PropertyRNA *prop; + static const EnumPropertyItem prop_shape_items[] = { + {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""}, + {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""}, + {CURVE_PRESET_MAX, "MAX", 0, "Max", ""}, + {CURVE_PRESET_LINE, "LINE", 0, "Line", ""}, + {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, + {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + ot->name = "Preset"; + ot->description = "Set brush shape"; + ot->idname = "BRUSH_OT_curve_preset"; + + ot->exec = brush_curve_preset_exec; + ot->poll = brush_curve_preset_poll; + + prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ } - /* face-select ops */ static int paint_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) { - paintface_select_linked(C, CTX_data_active_object(C), NULL, true); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; + paintface_select_linked(C, CTX_data_active_object(C), NULL, true); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; } void PAINT_OT_face_select_linked(wmOperatorType *ot) { - ot->name = "Select Linked"; - ot->description = "Select linked faces"; - ot->idname = "PAINT_OT_face_select_linked"; + ot->name = "Select Linked"; + ot->description = "Select linked faces"; + ot->idname = "PAINT_OT_face_select_linked"; - ot->exec = paint_select_linked_exec; - ot->poll = facemask_paint_poll; + ot->exec = paint_select_linked_exec; + ot->poll = facemask_paint_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static int paint_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - const bool select = !RNA_boolean_get(op->ptr, "deselect"); - view3d_operator_needs_opengl(C); - paintface_select_linked(C, CTX_data_active_object(C), event->mval, select); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; + const bool select = !RNA_boolean_get(op->ptr, "deselect"); + view3d_operator_needs_opengl(C); + paintface_select_linked(C, CTX_data_active_object(C), event->mval, select); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; } void PAINT_OT_face_select_linked_pick(wmOperatorType *ot) { - ot->name = "Select Linked Pick"; - ot->description = "Select linked faces under the cursor"; - ot->idname = "PAINT_OT_face_select_linked_pick"; + ot->name = "Select Linked Pick"; + ot->description = "Select linked faces under the cursor"; + ot->idname = "PAINT_OT_face_select_linked_pick"; - ot->invoke = paint_select_linked_pick_invoke; - ot->poll = facemask_paint_poll; + ot->invoke = paint_select_linked_pick_invoke; + ot->poll = facemask_paint_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items"); + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items"); } - static int face_select_all_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - if (paintface_deselect_all_visible(C, ob, RNA_enum_get(op->ptr, "action"), true)) { - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; + Object *ob = CTX_data_active_object(C); + if (paintface_deselect_all_visible(C, ob, RNA_enum_get(op->ptr, "action"), true)) { + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; } - void PAINT_OT_face_select_all(wmOperatorType *ot) { - ot->name = "(De)select All"; - ot->description = "Change selection for all faces"; - ot->idname = "PAINT_OT_face_select_all"; + ot->name = "(De)select All"; + ot->description = "Change selection for all faces"; + ot->idname = "PAINT_OT_face_select_all"; - ot->exec = face_select_all_exec; - ot->poll = facemask_paint_poll; + ot->exec = face_select_all_exec; + ot->poll = facemask_paint_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - WM_operator_properties_select_all(ot); + WM_operator_properties_select_all(ot); } - static int vert_select_all_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - paintvert_deselect_all_visible(ob, RNA_enum_get(op->ptr, "action"), true); - paintvert_tag_select_update(C, ob); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; + Object *ob = CTX_data_active_object(C); + paintvert_deselect_all_visible(ob, RNA_enum_get(op->ptr, "action"), true); + paintvert_tag_select_update(C, ob); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; } - void PAINT_OT_vert_select_all(wmOperatorType *ot) { - ot->name = "(De)select All"; - ot->description = "Change selection for all vertices"; - ot->idname = "PAINT_OT_vert_select_all"; + ot->name = "(De)select All"; + ot->description = "Change selection for all vertices"; + ot->idname = "PAINT_OT_vert_select_all"; - ot->exec = vert_select_all_exec; - ot->poll = vert_paint_poll; + ot->exec = vert_select_all_exec; + ot->poll = vert_paint_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - WM_operator_properties_select_all(ot); + WM_operator_properties_select_all(ot); } - static int vert_select_ungrouped_exec(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - Mesh *me = ob->data; - - if (BLI_listbase_is_empty(&ob->defbase) || (me->dvert == NULL)) { - BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object"); - return OPERATOR_CANCELLED; - } - - paintvert_select_ungrouped(ob, RNA_boolean_get(op->ptr, "extend"), true); - paintvert_tag_select_update(C, ob); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + + if (BLI_listbase_is_empty(&ob->defbase) || (me->dvert == NULL)) { + BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object"); + return OPERATOR_CANCELLED; + } + + paintvert_select_ungrouped(ob, RNA_boolean_get(op->ptr, "extend"), true); + paintvert_tag_select_update(C, ob); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; } void PAINT_OT_vert_select_ungrouped(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Select Ungrouped"; - ot->idname = "PAINT_OT_vert_select_ungrouped"; - ot->description = "Select vertices without a group"; + /* identifiers */ + ot->name = "Select Ungrouped"; + ot->idname = "PAINT_OT_vert_select_ungrouped"; + ot->description = "Select vertices without a group"; - /* api callbacks */ - ot->exec = vert_select_ungrouped_exec; - ot->poll = vert_paint_poll; + /* api callbacks */ + ot->exec = vert_select_ungrouped_exec; + ot->poll = vert_paint_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); } static int face_select_hide_exec(bContext *C, wmOperator *op) { - const bool unselected = RNA_boolean_get(op->ptr, "unselected"); - Object *ob = CTX_data_active_object(C); - paintface_hide(C, ob, unselected); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; + const bool unselected = RNA_boolean_get(op->ptr, "unselected"); + Object *ob = CTX_data_active_object(C); + paintface_hide(C, ob, unselected); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; } void PAINT_OT_face_select_hide(wmOperatorType *ot) { - ot->name = "Face Select Hide"; - ot->description = "Hide selected faces"; - ot->idname = "PAINT_OT_face_select_hide"; + ot->name = "Face Select Hide"; + ot->description = "Hide selected faces"; + ot->idname = "PAINT_OT_face_select_hide"; - ot->exec = face_select_hide_exec; - ot->poll = facemask_paint_poll; + ot->exec = face_select_hide_exec; + ot->poll = facemask_paint_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); + RNA_def_boolean( + ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects"); } static int face_select_reveal_exec(bContext *C, wmOperator *op) { - const bool select = RNA_boolean_get(op->ptr, "select"); - Object *ob = CTX_data_active_object(C); - paintface_reveal(C, ob, select); - ED_region_tag_redraw(CTX_wm_region(C)); - return OPERATOR_FINISHED; + const bool select = RNA_boolean_get(op->ptr, "select"); + Object *ob = CTX_data_active_object(C); + paintface_reveal(C, ob, select); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_FINISHED; } void PAINT_OT_face_select_reveal(wmOperatorType *ot) { - ot->name = "Face Select Reveal"; - ot->description = "Reveal hidden faces"; - ot->idname = "PAINT_OT_face_select_reveal"; + ot->name = "Face Select Reveal"; + ot->description = "Reveal hidden faces"; + ot->idname = "PAINT_OT_face_select_reveal"; - ot->exec = face_select_reveal_exec; - ot->poll = facemask_paint_poll; + ot->exec = face_select_reveal_exec; + ot->poll = facemask_paint_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "select", true, "Select", ""); + RNA_def_boolean(ot->srna, "select", true, "Select", ""); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 85bdba683cd..70783ff004e 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -77,130 +77,130 @@ #include "BKE_ccg.h" #include "sculpt_intern.h" -#include "paint_intern.h" /* own include */ +#include "paint_intern.h" /* own include */ /* Use for 'blur' brush, align with PBVH nodes, created and freed on each update. */ struct VPaintAverageAccum { - uint len; - uint value[3]; + uint len; + uint value[3]; }; struct WPaintAverageAccum { - uint len; - double value; + uint len; + double value; }; struct NormalAnglePrecalc { - bool do_mask_normal; - /* what angle to mask at */ - float angle; - /* cos(angle), faster to compare */ - float angle__cos; - float angle_inner; - float angle_inner__cos; - /* difference between angle and angle_inner, for easy access */ - float angle_range; + bool do_mask_normal; + /* what angle to mask at */ + float angle; + /* cos(angle), faster to compare */ + float angle__cos; + float angle_inner; + float angle_inner__cos; + /* difference between angle and angle_inner, for easy access */ + float angle_range; }; - -static void view_angle_limits_init( - struct NormalAnglePrecalc *a, float angle, bool do_mask_normal) +static void view_angle_limits_init(struct NormalAnglePrecalc *a, float angle, bool do_mask_normal) { - angle = RAD2DEGF(angle); - a->do_mask_normal = do_mask_normal; - if (do_mask_normal) { - a->angle_inner = angle; - a->angle = (a->angle_inner + 90.0f) * 0.5f; - } - else { - a->angle_inner = a->angle = angle; - } + angle = RAD2DEGF(angle); + a->do_mask_normal = do_mask_normal; + if (do_mask_normal) { + a->angle_inner = angle; + a->angle = (a->angle_inner + 90.0f) * 0.5f; + } + else { + a->angle_inner = a->angle = angle; + } - a->angle_inner *= (float)(M_PI_2 / 90); - a->angle *= (float)(M_PI_2 / 90); - a->angle_range = a->angle - a->angle_inner; + a->angle_inner *= (float)(M_PI_2 / 90); + a->angle *= (float)(M_PI_2 / 90); + a->angle_range = a->angle - a->angle_inner; - if (a->angle_range <= 0.0f) { - a->do_mask_normal = false; /* no need to do blending */ - } + if (a->angle_range <= 0.0f) { + a->do_mask_normal = false; /* no need to do blending */ + } - a->angle__cos = cosf(a->angle); - a->angle_inner__cos = cosf(a->angle_inner); + a->angle__cos = cosf(a->angle); + a->angle_inner__cos = cosf(a->angle_inner); } -static float view_angle_limits_apply_falloff( - const struct NormalAnglePrecalc *a, float angle_cos, float *mask_p) +static float view_angle_limits_apply_falloff(const struct NormalAnglePrecalc *a, + float angle_cos, + float *mask_p) { - if (angle_cos <= a->angle__cos) { - /* outsize the normal limit */ - return false; - } - else if (angle_cos < a->angle_inner__cos) { - *mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range; - return true; - } - else { - return true; - } + if (angle_cos <= a->angle__cos) { + /* outsize the normal limit */ + return false; + } + else if (angle_cos < a->angle_inner__cos) { + *mask_p *= (a->angle - acosf(angle_cos)) / a->angle_range; + return true; + } + else { + return true; + } } static bool vwpaint_use_normal(const VPaint *vp) { - return ((vp->paint.brush->flag & BRUSH_FRONTFACE) != 0) || - ((vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + return ((vp->paint.brush->flag & BRUSH_FRONTFACE) != 0) || + ((vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); } static bool brush_use_accumulate_ex(const Brush *brush, const int ob_mode) { - return ((brush->flag & BRUSH_ACCUMULATE) != 0 || - (ob_mode == OB_MODE_VERTEX_PAINT ? - (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) : - (brush->weightpaint_tool == WPAINT_TOOL_SMEAR))); + return ((brush->flag & BRUSH_ACCUMULATE) != 0 || + (ob_mode == OB_MODE_VERTEX_PAINT ? (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) : + (brush->weightpaint_tool == WPAINT_TOOL_SMEAR))); } static bool brush_use_accumulate(const VPaint *vp) { - return brush_use_accumulate_ex(vp->paint.brush, vp->paint.runtime.ob_mode); + return brush_use_accumulate_ex(vp->paint.brush, vp->paint.runtime.ob_mode); } -static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev, MDeformVert *dvert_curr, int index) +static MDeformVert *defweight_prev_init(MDeformVert *dvert_prev, + MDeformVert *dvert_curr, + int index) { - MDeformVert *dv_curr = &dvert_curr[index]; - MDeformVert *dv_prev = &dvert_prev[index]; - if (dv_prev->flag == 1) { - dv_prev->flag = 0; - defvert_copy(dv_prev, dv_curr); - } - return dv_prev; + MDeformVert *dv_curr = &dvert_curr[index]; + MDeformVert *dv_prev = &dvert_prev[index]; + if (dv_prev->flag == 1) { + dv_prev->flag = 0; + defvert_copy(dv_prev, dv_curr); + } + return dv_prev; } /* check if we can do partial updates and have them draw realtime * (without evaluating modifiers) */ static bool vertex_paint_use_fast_update_check(Object *ob) { - Mesh *me_eval = ob->runtime.mesh_eval; + Mesh *me_eval = ob->runtime.mesh_eval; - if (me_eval != NULL) { - Mesh *me = BKE_mesh_from_object(ob); - if (me && me->mloopcol) { - return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_MLOOPCOL)); - } - } + if (me_eval != NULL) { + Mesh *me = BKE_mesh_from_object(ob); + if (me && me->mloopcol) { + return (me->mloopcol == CustomData_get_layer(&me_eval->ldata, CD_MLOOPCOL)); + } + } - return false; + return false; } static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval[2]) { - const int mval_i[2] = {mval[0], mval[1]}; - float world[3]; + const int mval_i[2] = {mval[0], mval[1]}; + float world[3]; - if (ED_view3d_autodist_simple(ar, mval_i, world, 0, NULL)) { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->average_stroke_counter++; - add_v3_v3(ups->average_stroke_accum, world); - ups->last_stroke_valid = true; - } + if (ED_view3d_autodist_simple(ar, mval_i, world, 0, NULL)) { + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + ups->average_stroke_counter++; + add_v3_v3(ups->average_stroke_accum, world); + ups->last_stroke_valid = true; + } } /* polling - retrieve whether cursor should be set or operator should be done */ @@ -208,349 +208,360 @@ static void paint_last_stroke_update(Scene *scene, ARegion *ar, const float mval /* Returns true if vertex paint mode is active */ bool vertex_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly; + return ob && ob->mode == OB_MODE_VERTEX_PAINT && ((Mesh *)ob->data)->totpoly; } static bool vertex_paint_poll_ex(bContext *C, bool check_tool) { - if (vertex_paint_mode_poll(C) && - BKE_paint_brush(&CTX_data_tool_settings(C)->vpaint->paint)) - { - ScrArea *sa = CTX_wm_area(C); - if (sa && sa->spacetype == SPACE_VIEW3D) { - ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { - if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { - return 1; - } - } - } - } - return 0; + if (vertex_paint_mode_poll(C) && BKE_paint_brush(&CTX_data_tool_settings(C)->vpaint->paint)) { + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype == SPACE_VIEW3D) { + ARegion *ar = CTX_wm_region(C); + if (ar->regiontype == RGN_TYPE_WINDOW) { + if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { + return 1; + } + } + } + } + return 0; } bool vertex_paint_poll(bContext *C) { - return vertex_paint_poll_ex(C, true); + return vertex_paint_poll_ex(C, true); } bool vertex_paint_poll_ignore_tool(bContext *C) { - return vertex_paint_poll_ex(C, false); + return vertex_paint_poll_ex(C, false); } bool weight_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly; + return ob && ob->mode == OB_MODE_WEIGHT_PAINT && ((Mesh *)ob->data)->totpoly; } static bool weight_paint_poll_ex(bContext *C, bool check_tool) { - Object *ob = CTX_data_active_object(C); - ScrArea *sa; - - if ((ob != NULL) && - (ob->mode & OB_MODE_WEIGHT_PAINT) && - (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && - (sa = CTX_wm_area(C)) && - (sa->spacetype == SPACE_VIEW3D)) - { - ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { - if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { - return 1; - } - } - } - return 0; + Object *ob = CTX_data_active_object(C); + ScrArea *sa; + + if ((ob != NULL) && (ob->mode & OB_MODE_WEIGHT_PAINT) && + (BKE_paint_brush(&CTX_data_tool_settings(C)->wpaint->paint) != NULL) && + (sa = CTX_wm_area(C)) && (sa->spacetype == SPACE_VIEW3D)) { + ARegion *ar = CTX_wm_region(C); + if (ar->regiontype == RGN_TYPE_WINDOW) { + if (!check_tool || WM_toolsystem_active_tool_is_brush(C)) { + return 1; + } + } + } + return 0; } bool weight_paint_poll(bContext *C) { - return weight_paint_poll_ex(C, true); + return weight_paint_poll_ex(C, true); } bool weight_paint_poll_ignore_tool(bContext *C) { - return weight_paint_poll_ex(C, false); + return weight_paint_poll_ex(C, false); } uint vpaint_get_current_col(Scene *scene, VPaint *vp, bool secondary) { - Brush *brush = BKE_paint_brush(&vp->paint); - uchar col[4]; - rgb_float_to_uchar(col, secondary ? BKE_brush_secondary_color_get(scene, brush) : BKE_brush_color_get(scene, brush)); - col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ - return *(uint *)col; + Brush *brush = BKE_paint_brush(&vp->paint); + uchar col[4]; + rgb_float_to_uchar(col, + secondary ? BKE_brush_secondary_color_get(scene, brush) : + BKE_brush_color_get(scene, brush)); + col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ + return *(uint *)col; } /* wpaint has 'wpaint_blend' */ -static uint vpaint_blend( - const VPaint *vp, uint color_curr, uint color_orig, - uint color_paint, const int alpha_i, - /* pre scaled from [0-1] --> [0-255] */ - const int brush_alpha_value_i) -{ - const Brush *brush = vp->paint.brush; - const IMB_BlendMode blend = brush->blend; - - uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); - - /* if no accumulate, clip color adding with colorig & orig alpha */ - if (!brush_use_accumulate(vp)) { - uint color_test, a; - char *cp, *ct, *co; - - color_test = ED_vpaint_blend_tool(blend, color_orig, color_paint, brush_alpha_value_i); - - cp = (char *)&color_blend; - ct = (char *)&color_test; - co = (char *)&color_orig; - - for (a = 0; a < 4; a++) { - if (ct[a] < co[a]) { - if (cp[a] < ct[a]) cp[a] = ct[a]; - else if (cp[a] > co[a]) cp[a] = co[a]; - } - else { - if (cp[a] < co[a]) cp[a] = co[a]; - else if (cp[a] > ct[a]) cp[a] = ct[a]; - } - } - } - - if ((brush->flag & BRUSH_LOCK_ALPHA) && - !ELEM(blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA)) - { - char *cp, *cc; - cp = (char *)&color_blend; - cc = (char *)&color_curr; - cp[3] = cc[3]; - } - - return color_blend; -} - -static void tex_color_alpha( - VPaint *vp, const ViewContext *vc, const float co[3], - float r_rgba[4]) -{ - const Brush *brush = BKE_paint_brush(&vp->paint); - BLI_assert(brush->mtex.tex != NULL); - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { - BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL); - } - else { - float co_ss[2]; /* screenspace */ - if (ED_view3d_project_float_object( - vc->ar, - co, co_ss, - V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) - { - const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ - BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL); - } - else { - zero_v4(r_rgba); - } - } +static uint vpaint_blend(const VPaint *vp, + uint color_curr, + uint color_orig, + uint color_paint, + const int alpha_i, + /* pre scaled from [0-1] --> [0-255] */ + const int brush_alpha_value_i) +{ + const Brush *brush = vp->paint.brush; + const IMB_BlendMode blend = brush->blend; + + uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); + + /* if no accumulate, clip color adding with colorig & orig alpha */ + if (!brush_use_accumulate(vp)) { + uint color_test, a; + char *cp, *ct, *co; + + color_test = ED_vpaint_blend_tool(blend, color_orig, color_paint, brush_alpha_value_i); + + cp = (char *)&color_blend; + ct = (char *)&color_test; + co = (char *)&color_orig; + + for (a = 0; a < 4; a++) { + if (ct[a] < co[a]) { + if (cp[a] < ct[a]) + cp[a] = ct[a]; + else if (cp[a] > co[a]) + cp[a] = co[a]; + } + else { + if (cp[a] < co[a]) + cp[a] = co[a]; + else if (cp[a] > ct[a]) + cp[a] = ct[a]; + } + } + } + + if ((brush->flag & BRUSH_LOCK_ALPHA) && + !ELEM(blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA)) { + char *cp, *cc; + cp = (char *)&color_blend; + cc = (char *)&color_curr; + cp[3] = cc[3]; + } + + return color_blend; +} + +static void tex_color_alpha(VPaint *vp, const ViewContext *vc, const float co[3], float r_rgba[4]) +{ + const Brush *brush = BKE_paint_brush(&vp->paint); + BLI_assert(brush->mtex.tex != NULL); + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_3D) { + BKE_brush_sample_tex_3d(vc->scene, brush, co, r_rgba, 0, NULL); + } + else { + float co_ss[2]; /* screenspace */ + if (ED_view3d_project_float_object( + vc->ar, co, co_ss, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == + V3D_PROJ_RET_OK) { + const float co_ss_3d[3] = {co_ss[0], co_ss[1], 0.0f}; /* we need a 3rd empty value */ + BKE_brush_sample_tex_3d(vc->scene, brush, co_ss_3d, r_rgba, 0, NULL); + } + else { + zero_v4(r_rgba); + } + } } /* vpaint has 'vpaint_blend' */ -static float wpaint_blend( - const VPaint *wp, float weight, - const float alpha, float paintval, - const float UNUSED(brush_alpha_value), - const short do_flip) -{ - const Brush *brush = wp->paint.brush; - IMB_BlendMode blend = brush->blend; - - if (do_flip) { - switch (blend) { - case IMB_BLEND_MIX: - paintval = 1.f - paintval; break; - case IMB_BLEND_ADD: - blend = IMB_BLEND_SUB; break; - case IMB_BLEND_SUB: - blend = IMB_BLEND_ADD; break; - case IMB_BLEND_LIGHTEN: - blend = IMB_BLEND_DARKEN; break; - case IMB_BLEND_DARKEN: - blend = IMB_BLEND_LIGHTEN; break; - default: break; - } - } - - weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha); - - CLAMP(weight, 0.0f, 1.0f); - - return weight; +static float wpaint_blend(const VPaint *wp, + float weight, + const float alpha, + float paintval, + const float UNUSED(brush_alpha_value), + const short do_flip) +{ + const Brush *brush = wp->paint.brush; + IMB_BlendMode blend = brush->blend; + + if (do_flip) { + switch (blend) { + case IMB_BLEND_MIX: + paintval = 1.f - paintval; + break; + case IMB_BLEND_ADD: + blend = IMB_BLEND_SUB; + break; + case IMB_BLEND_SUB: + blend = IMB_BLEND_ADD; + break; + case IMB_BLEND_LIGHTEN: + blend = IMB_BLEND_DARKEN; + break; + case IMB_BLEND_DARKEN: + blend = IMB_BLEND_LIGHTEN; + break; + default: + break; + } + } + + weight = ED_wpaint_blend_tool(blend, weight, paintval, alpha); + + CLAMP(weight, 0.0f, 1.0f); + + return weight; } static float wpaint_clamp_monotonic(float oldval, float curval, float newval) { - if (newval < oldval) - return MIN2(newval, curval); - else if (newval > oldval) - return MAX2(newval, curval); - else - return newval; + if (newval < oldval) + return MIN2(newval, curval); + else if (newval > oldval) + return MAX2(newval, curval); + else + return newval; } /* ----------------------------------------------------- */ -static void do_weight_paint_normalize_all(MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap) -{ - float sum = 0.0f, fac; - uint i, tot = 0; - MDeformWeight *dw; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - tot++; - sum += dw->weight; - } - } - - if ((tot == 0) || (sum == 1.0f)) { - return; - } - - if (sum != 0.0f) { - fac = 1.0f / sum; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - dw->weight *= fac; - } - } - } - else { - /* hrmf, not a factor in this case */ - fac = 1.0f / tot; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - dw->weight = fac; - } - } - } +static void do_weight_paint_normalize_all(MDeformVert *dvert, + const int defbase_tot, + const bool *vgroup_validmap) +{ + float sum = 0.0f, fac; + uint i, tot = 0; + MDeformWeight *dw; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + tot++; + sum += dw->weight; + } + } + + if ((tot == 0) || (sum == 1.0f)) { + return; + } + + if (sum != 0.0f) { + fac = 1.0f / sum; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + dw->weight *= fac; + } + } + } + else { + /* hrmf, not a factor in this case */ + fac = 1.0f / tot; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + dw->weight = fac; + } + } + } } /** * A version of #do_weight_paint_normalize_all that includes locked weights * but only changes unlocked weights. */ -static bool do_weight_paint_normalize_all_locked( - MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap, - const bool *lock_flags) -{ - float sum = 0.0f, fac; - float sum_unlock = 0.0f; - float lock_weight = 0.0f; - uint i, tot = 0; - MDeformWeight *dw; - - if (lock_flags == NULL) { - do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap); - return true; - } - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - sum += dw->weight; - - if (lock_flags[dw->def_nr]) { - lock_weight += dw->weight; - } - else { - tot++; - sum_unlock += dw->weight; - } - } - } - - if (sum == 1.0f) { - return true; - } - - if (tot == 0) { - return false; - } - - if (lock_weight >= 1.0f) { - /* locked groups make it impossible to fully normalize, - * zero out what we can and return false */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - if (lock_flags[dw->def_nr] == false) { - dw->weight = 0.0f; - } - } - } - - return (lock_weight == 1.0f); - } - else if (sum_unlock != 0.0f) { - fac = (1.0f - lock_weight) / sum_unlock; - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - if (lock_flags[dw->def_nr] == false) { - dw->weight *= fac; - /* paranoid but possibly with float error */ - CLAMP(dw->weight, 0.0f, 1.0f); - } - } - } - } - else { - /* hrmf, not a factor in this case */ - fac = (1.0f - lock_weight) / tot; - /* paranoid but possibly with float error */ - CLAMP(fac, 0.0f, 1.0f); - - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { - if (lock_flags[dw->def_nr] == false) { - dw->weight = fac; - } - } - } - } - - return true; +static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert, + const int defbase_tot, + const bool *vgroup_validmap, + const bool *lock_flags) +{ + float sum = 0.0f, fac; + float sum_unlock = 0.0f; + float lock_weight = 0.0f; + uint i, tot = 0; + MDeformWeight *dw; + + if (lock_flags == NULL) { + do_weight_paint_normalize_all(dvert, defbase_tot, vgroup_validmap); + return true; + } + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + sum += dw->weight; + + if (lock_flags[dw->def_nr]) { + lock_weight += dw->weight; + } + else { + tot++; + sum_unlock += dw->weight; + } + } + } + + if (sum == 1.0f) { + return true; + } + + if (tot == 0) { + return false; + } + + if (lock_weight >= 1.0f) { + /* locked groups make it impossible to fully normalize, + * zero out what we can and return false */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + if (lock_flags[dw->def_nr] == false) { + dw->weight = 0.0f; + } + } + } + + return (lock_weight == 1.0f); + } + else if (sum_unlock != 0.0f) { + fac = (1.0f - lock_weight) / sum_unlock; + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + if (lock_flags[dw->def_nr] == false) { + dw->weight *= fac; + /* paranoid but possibly with float error */ + CLAMP(dw->weight, 0.0f, 1.0f); + } + } + } + } + else { + /* hrmf, not a factor in this case */ + fac = (1.0f - lock_weight) / tot; + /* paranoid but possibly with float error */ + CLAMP(fac, 0.0f, 1.0f); + + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && vgroup_validmap[dw->def_nr]) { + if (lock_flags[dw->def_nr] == false) { + dw->weight = fac; + } + } + } + } + + return true; } /** * \note same as function above except it does a second pass without active group * if normalize fails with it. */ -static void do_weight_paint_normalize_all_locked_try_active( - MDeformVert *dvert, const int defbase_tot, const bool *vgroup_validmap, - const bool *lock_flags, const bool *lock_with_active) -{ - /* first pass with both active and explicitly locked groups restricted from change */ - - bool success = do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_with_active); - - if (!success) { - /** - * Locks prevented the first pass from full completion, so remove restriction on active group; e.g: - * - * - With 1.0 weight painted into active: - * nonzero locked weight; first pass zeroed out unlocked weight; scale 1 down to fit. - * - With 0.0 weight painted into active: - * no unlocked groups; first pass did nothing; increase 0 to fit. - */ - do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags); - } +static void do_weight_paint_normalize_all_locked_try_active(MDeformVert *dvert, + const int defbase_tot, + const bool *vgroup_validmap, + const bool *lock_flags, + const bool *lock_with_active) +{ + /* first pass with both active and explicitly locked groups restricted from change */ + + bool success = do_weight_paint_normalize_all_locked( + dvert, defbase_tot, vgroup_validmap, lock_with_active); + + if (!success) { + /** + * Locks prevented the first pass from full completion, so remove restriction on active group; e.g: + * + * - With 1.0 weight painted into active: + * nonzero locked weight; first pass zeroed out unlocked weight; scale 1 down to fit. + * - With 0.0 weight painted into active: + * no unlocked groups; first pass did nothing; increase 0 to fit. + */ + do_weight_paint_normalize_all_locked(dvert, defbase_tot, vgroup_validmap, lock_flags); + } } #if 0 /* UNUSED */ @@ -558,101 +569,108 @@ static bool has_unselected_unlocked_bone_group( int defbase_tot, bool *defbase_sel, int selected, const bool *lock_flags, const bool *vgroup_validmap) { - int i; - if (defbase_tot == selected) { - return false; - } - for (i = 0; i < defbase_tot; i++) { - if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) { - return true; - } - } - return false; + int i; + if (defbase_tot == selected) { + return false; + } + for (i = 0; i < defbase_tot; i++) { + if (vgroup_validmap[i] && !defbase_sel[i] && !lock_flags[i]) { + return true; + } + } + return false; } #endif -static void multipaint_clamp_change( - MDeformVert *dvert, const int defbase_tot, const bool *defbase_sel, - float *change_p) -{ - int i; - MDeformWeight *dw; - float val; - float change = *change_p; - - /* verify that the change does not cause values exceeding 1 and clamp it */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { - if (dw->weight) { - val = dw->weight * change; - if (val > 1) { - change = 1.0f / dw->weight; - } - } - } - } - - *change_p = change; -} - -static bool multipaint_verify_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel) -{ - int i; - MDeformWeight *dw; - float val; - - /* in case the change is reduced, you need to recheck - * the earlier values to make sure they are not 0 - * (precision error) */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { - if (dw->weight) { - val = dw->weight * change; - /* the value should never reach zero while multi-painting if it - * was nonzero beforehand */ - if (val <= 0) { - return false; - } - } - } - } - - return true; -} - -static void multipaint_apply_change(MDeformVert *dvert, const int defbase_tot, float change, const bool *defbase_sel) -{ - int i; - MDeformWeight *dw; - - /* apply the valid change */ - for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { - if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { - if (dw->weight) { - dw->weight = dw->weight * change; - CLAMP(dw->weight, 0.0f, 1.0f); - } - } - } +static void multipaint_clamp_change(MDeformVert *dvert, + const int defbase_tot, + const bool *defbase_sel, + float *change_p) +{ + int i; + MDeformWeight *dw; + float val; + float change = *change_p; + + /* verify that the change does not cause values exceeding 1 and clamp it */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { + if (dw->weight) { + val = dw->weight * change; + if (val > 1) { + change = 1.0f / dw->weight; + } + } + } + } + + *change_p = change; +} + +static bool multipaint_verify_change(MDeformVert *dvert, + const int defbase_tot, + float change, + const bool *defbase_sel) +{ + int i; + MDeformWeight *dw; + float val; + + /* in case the change is reduced, you need to recheck + * the earlier values to make sure they are not 0 + * (precision error) */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { + if (dw->weight) { + val = dw->weight * change; + /* the value should never reach zero while multi-painting if it + * was nonzero beforehand */ + if (val <= 0) { + return false; + } + } + } + } + + return true; +} + +static void multipaint_apply_change(MDeformVert *dvert, + const int defbase_tot, + float change, + const bool *defbase_sel) +{ + int i; + MDeformWeight *dw; + + /* apply the valid change */ + for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) { + if (dw->def_nr < defbase_tot && defbase_sel[dw->def_nr]) { + if (dw->weight) { + dw->weight = dw->weight * change; + CLAMP(dw->weight, 0.0f, 1.0f); + } + } + } } /** * Variables stored both for 'active' and 'mirror' sides. */ struct WeightPaintGroupData { - /** index of active group or its mirror - * - * - 'active' is always `ob->actdef`. - * - 'mirror' is -1 when 'ME_EDIT_MIRROR_X' flag id disabled, - * otherwise this will be set to the mirror or the active group (if the group isn't mirrored). - */ - int index; - /** lock that includes the 'index' as locked too - * - * - 'active' is set of locked or active/selected groups - * - 'mirror' is set of locked or mirror groups - */ - const bool *lock; + /** index of active group or its mirror + * + * - 'active' is always `ob->actdef`. + * - 'mirror' is -1 when 'ME_EDIT_MIRROR_X' flag id disabled, + * otherwise this will be set to the mirror or the active group (if the group isn't mirrored). + */ + int index; + /** lock that includes the 'index' as locked too + * + * - 'active' is set of locked or active/selected groups + * - 'mirror' is set of locked or mirror groups + */ + const bool *lock; }; /* struct to avoid passing many args each call to do_weight_paint_vertex() @@ -661,490 +679,504 @@ struct WeightPaintGroupData { * paint stroke update - campbell */ typedef struct WeightPaintInfo { - int defbase_tot; + int defbase_tot; - /* both must add up to 'defbase_tot' */ - int defbase_tot_sel; - int defbase_tot_unsel; + /* both must add up to 'defbase_tot' */ + int defbase_tot_sel; + int defbase_tot_unsel; - struct WeightPaintGroupData active, mirror; + struct WeightPaintGroupData active, mirror; - /* boolean array for locked bones, - * length of defbase_tot */ - const bool *lock_flags; - /* boolean array for selected bones, - * length of defbase_tot, cant be const because of how its passed */ - const bool *defbase_sel; - /* same as WeightPaintData.vgroup_validmap, - * only added here for convenience */ - const bool *vgroup_validmap; + /* boolean array for locked bones, + * length of defbase_tot */ + const bool *lock_flags; + /* boolean array for selected bones, + * length of defbase_tot, cant be const because of how its passed */ + const bool *defbase_sel; + /* same as WeightPaintData.vgroup_validmap, + * only added here for convenience */ + const bool *vgroup_validmap; - bool do_flip; - bool do_multipaint; - bool do_auto_normalize; + bool do_flip; + bool do_multipaint; + bool do_auto_normalize; - float brush_alpha_value; /* result of BKE_brush_alpha_get() */ + float brush_alpha_value; /* result of BKE_brush_alpha_get() */ } WeightPaintInfo; static void do_weight_paint_vertex_single( - /* vars which remain the same for every vert */ - const VPaint *wp, Object *ob, const WeightPaintInfo *wpi, - /* vars which change on each stroke */ - const uint index, float alpha, float paintweight) -{ - Mesh *me = ob->data; - MDeformVert *dv = &me->dvert[index]; - bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - - MDeformWeight *dw; - float weight_prev; - - /* mirror vars */ - int index_mirr; - int vgroup_mirr; - - MDeformVert *dv_mirr; - MDeformWeight *dw_mirr; - - /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */ - if (me->editflag & ME_EDIT_MIRROR_X) { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); - vgroup_mirr = wpi->mirror.index; - - /* another possible error - mirror group _and_ active group are the same (which is fine), - * but we also are painting onto a center vertex - this would paint the same weight twice */ - if (index_mirr == index && vgroup_mirr == wpi->active.index) { - index_mirr = vgroup_mirr = -1; - } - } - else { - index_mirr = vgroup_mirr = -1; - } - - if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { - dw = defvert_find_index(dv, wpi->active.index); - } - else { - dw = defvert_verify_index(dv, wpi->active.index); - } - - if (dw == NULL) { - return; - } - - /* get the mirror def vars */ - if (index_mirr != -1) { - dv_mirr = &me->dvert[index_mirr]; - if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { - dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr); - - if (dw_mirr == NULL) { - index_mirr = vgroup_mirr = -1; - dv_mirr = NULL; - } - } - else { - if (index != index_mirr) { - dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); - } - else { - /* dv and dv_mirr are the same */ - int totweight_prev = dv_mirr->totweight; - int dw_offset = (int)(dw - dv_mirr->dw); - dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); - - /* if we added another, get our old one back */ - if (totweight_prev != dv_mirr->totweight) { - dw = &dv_mirr->dw[dw_offset]; - } - } - } - } - else { - dv_mirr = NULL; - dw_mirr = NULL; - } - - if (!brush_use_accumulate(wp)) { - MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; - MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index); - if (index_mirr != -1) { - defweight_prev_init(dvert_prev, me->dvert, index_mirr); - } - - weight_prev = defvert_find_weight(dv_prev, wpi->active.index); - } - else { - weight_prev = dw->weight; - } - - /* If there are no normalize-locks or multipaint, - * then there is no need to run the more complicated checks */ - - { - float new_weight = wpaint_blend( - wp, weight_prev, alpha, paintweight, - wpi->brush_alpha_value, wpi->do_flip); - - dw->weight = wpaint_clamp_monotonic(weight_prev, dw->weight, new_weight); - - /* WATCH IT: take care of the ordering of applying mirror -> normalize, - * can give wrong results [#26193], least confusing if normalize is done last */ - - /* apply mirror */ - if (index_mirr != -1) { - /* copy, not paint again */ - dw_mirr->weight = dw->weight; - } - - /* apply normalize */ - if (wpi->do_auto_normalize) { - /* note on normalize - this used to be applied after painting and normalize all weights, - * in some ways this is good because there is feedback where the more weights involved would - * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active. - * - * However this gave a problem since applying mirror, then normalize both verts - * the resulting weight wont match on both sides. - * - * If this 'resisting', slower normalize is nicer, we could call - * do_weight_paint_normalize_all() and only use... - * do_weight_paint_normalize_all_active() when normalizing the mirror vertex. - * - campbell - */ - do_weight_paint_normalize_all_locked_try_active( - dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - - if (index_mirr != -1) { - /* only normalize if this is not a center vertex, - * else we get a conflict, normalizing twice */ - if (index != index_mirr) { - do_weight_paint_normalize_all_locked_try_active( - dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->mirror.lock); - } - else { - /* this case accounts for... - * - painting onto a center vertex of a mesh - * - x mirror is enabled - * - auto normalize is enabled - * - the group you are painting onto has a L / R version - * - * We want L/R vgroups to have the same weight but this cant be if both are over 0.5, - * We _could_ have special check for that, but this would need its own normalize function which - * holds 2 groups from changing at once. - * - * So! just balance out the 2 weights, it keeps them equal and everything normalized. - * - * While it wont hit the desired weight immediately as the user waggles their mouse, - * constant painting and re-normalizing will get there. this is also just simpler logic. - * - campbell */ - dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f; - } - } - } - } + /* vars which remain the same for every vert */ + const VPaint *wp, + Object *ob, + const WeightPaintInfo *wpi, + /* vars which change on each stroke */ + const uint index, + float alpha, + float paintweight) +{ + Mesh *me = ob->data; + MDeformVert *dv = &me->dvert[index]; + bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + MDeformWeight *dw; + float weight_prev; + + /* mirror vars */ + int index_mirr; + int vgroup_mirr; + + MDeformVert *dv_mirr; + MDeformWeight *dw_mirr; + + /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */ + if (me->editflag & ME_EDIT_MIRROR_X) { + index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); + vgroup_mirr = wpi->mirror.index; + + /* another possible error - mirror group _and_ active group are the same (which is fine), + * but we also are painting onto a center vertex - this would paint the same weight twice */ + if (index_mirr == index && vgroup_mirr == wpi->active.index) { + index_mirr = vgroup_mirr = -1; + } + } + else { + index_mirr = vgroup_mirr = -1; + } + + if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { + dw = defvert_find_index(dv, wpi->active.index); + } + else { + dw = defvert_verify_index(dv, wpi->active.index); + } + + if (dw == NULL) { + return; + } + + /* get the mirror def vars */ + if (index_mirr != -1) { + dv_mirr = &me->dvert[index_mirr]; + if (wp->flag & VP_FLAG_VGROUP_RESTRICT) { + dw_mirr = defvert_find_index(dv_mirr, vgroup_mirr); + + if (dw_mirr == NULL) { + index_mirr = vgroup_mirr = -1; + dv_mirr = NULL; + } + } + else { + if (index != index_mirr) { + dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); + } + else { + /* dv and dv_mirr are the same */ + int totweight_prev = dv_mirr->totweight; + int dw_offset = (int)(dw - dv_mirr->dw); + dw_mirr = defvert_verify_index(dv_mirr, vgroup_mirr); + + /* if we added another, get our old one back */ + if (totweight_prev != dv_mirr->totweight) { + dw = &dv_mirr->dw[dw_offset]; + } + } + } + } + else { + dv_mirr = NULL; + dw_mirr = NULL; + } + + if (!brush_use_accumulate(wp)) { + MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; + MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index); + if (index_mirr != -1) { + defweight_prev_init(dvert_prev, me->dvert, index_mirr); + } + + weight_prev = defvert_find_weight(dv_prev, wpi->active.index); + } + else { + weight_prev = dw->weight; + } + + /* If there are no normalize-locks or multipaint, + * then there is no need to run the more complicated checks */ + + { + float new_weight = wpaint_blend( + wp, weight_prev, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip); + + dw->weight = wpaint_clamp_monotonic(weight_prev, dw->weight, new_weight); + + /* WATCH IT: take care of the ordering of applying mirror -> normalize, + * can give wrong results [#26193], least confusing if normalize is done last */ + + /* apply mirror */ + if (index_mirr != -1) { + /* copy, not paint again */ + dw_mirr->weight = dw->weight; + } + + /* apply normalize */ + if (wpi->do_auto_normalize) { + /* note on normalize - this used to be applied after painting and normalize all weights, + * in some ways this is good because there is feedback where the more weights involved would + * 'resist' so you couldn't instantly zero out other weights by painting 1.0 on the active. + * + * However this gave a problem since applying mirror, then normalize both verts + * the resulting weight wont match on both sides. + * + * If this 'resisting', slower normalize is nicer, we could call + * do_weight_paint_normalize_all() and only use... + * do_weight_paint_normalize_all_active() when normalizing the mirror vertex. + * - campbell + */ + do_weight_paint_normalize_all_locked_try_active( + dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); + + if (index_mirr != -1) { + /* only normalize if this is not a center vertex, + * else we get a conflict, normalizing twice */ + if (index != index_mirr) { + do_weight_paint_normalize_all_locked_try_active( + dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->mirror.lock); + } + else { + /* this case accounts for... + * - painting onto a center vertex of a mesh + * - x mirror is enabled + * - auto normalize is enabled + * - the group you are painting onto has a L / R version + * + * We want L/R vgroups to have the same weight but this cant be if both are over 0.5, + * We _could_ have special check for that, but this would need its own normalize function which + * holds 2 groups from changing at once. + * + * So! just balance out the 2 weights, it keeps them equal and everything normalized. + * + * While it wont hit the desired weight immediately as the user waggles their mouse, + * constant painting and re-normalizing will get there. this is also just simpler logic. + * - campbell */ + dw_mirr->weight = dw->weight = (dw_mirr->weight + dw->weight) * 0.5f; + } + } + } + } } static void do_weight_paint_vertex_multi( - /* vars which remain the same for every vert */ - const VPaint *wp, Object *ob, const WeightPaintInfo *wpi, - /* vars which change on each stroke */ - const uint index, float alpha, float paintweight) -{ - Mesh *me = ob->data; - MDeformVert *dv = &me->dvert[index]; - bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - - /* mirror vars */ - int index_mirr = -1; - MDeformVert *dv_mirr = NULL; - - /* weights */ - float curw, oldw, neww, change, curw_mirr, change_mirr; - - /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */ - if (me->editflag & ME_EDIT_MIRROR_X) { - index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); - - if (index_mirr != -1 && index_mirr != index) { - dv_mirr = &me->dvert[index_mirr]; - } - else { - index_mirr = -1; - } - } - - /* compute weight change by applying the brush to average or sum of group weights */ - curw = BKE_defvert_multipaint_collective_weight( - dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); - - if (curw == 0.0f) { - /* note: no weight to assign to this vertex, could add all groups? */ - return; - } - - if (!brush_use_accumulate(wp)) { - MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; - MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index); - if (index_mirr != -1) { - defweight_prev_init(dvert_prev, me->dvert, index_mirr); - } - - oldw = BKE_defvert_multipaint_collective_weight( - dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); - } - else { - oldw = curw; - } - - neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip); - neww = wpaint_clamp_monotonic(oldw, curw, neww); - - change = neww / curw; - - /* verify for all groups that 0 < result <= 1 */ - multipaint_clamp_change(dv, wpi->defbase_tot, wpi->defbase_sel, &change); - - if (dv_mirr != NULL) { - curw_mirr = BKE_defvert_multipaint_collective_weight( - dv_mirr, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); - - if (curw_mirr == 0.0f) { - /* can't mirror into a zero weight vertex */ - dv_mirr = NULL; - } - else { - /* mirror is changed to achieve the same collective weight value */ - float orig = change_mirr = curw * change / curw_mirr; - - multipaint_clamp_change(dv_mirr, wpi->defbase_tot, wpi->defbase_sel, &change_mirr); - - if (!multipaint_verify_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel)) { - return; - } - - change *= change_mirr / orig; - } - } - - if (!multipaint_verify_change(dv, wpi->defbase_tot, change, wpi->defbase_sel)) { - return; - } - - /* apply validated change to vertex and mirror */ - multipaint_apply_change(dv, wpi->defbase_tot, change, wpi->defbase_sel); - - if (dv_mirr != NULL) { - multipaint_apply_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel); - } - - /* normalize */ - if (wpi->do_auto_normalize) { - do_weight_paint_normalize_all_locked_try_active( - dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - - if (dv_mirr != NULL) { - do_weight_paint_normalize_all_locked_try_active( - dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); - } - } + /* vars which remain the same for every vert */ + const VPaint *wp, + Object *ob, + const WeightPaintInfo *wpi, + /* vars which change on each stroke */ + const uint index, + float alpha, + float paintweight) +{ + Mesh *me = ob->data; + MDeformVert *dv = &me->dvert[index]; + bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + /* mirror vars */ + int index_mirr = -1; + MDeformVert *dv_mirr = NULL; + + /* weights */ + float curw, oldw, neww, change, curw_mirr, change_mirr; + + /* from now on we can check if mirrors enabled if this var is -1 and not bother with the flag */ + if (me->editflag & ME_EDIT_MIRROR_X) { + index_mirr = mesh_get_x_mirror_vert(ob, NULL, index, topology); + + if (index_mirr != -1 && index_mirr != index) { + dv_mirr = &me->dvert[index_mirr]; + } + else { + index_mirr = -1; + } + } + + /* compute weight change by applying the brush to average or sum of group weights */ + curw = BKE_defvert_multipaint_collective_weight( + dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); + + if (curw == 0.0f) { + /* note: no weight to assign to this vertex, could add all groups? */ + return; + } + + if (!brush_use_accumulate(wp)) { + MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev; + MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index); + if (index_mirr != -1) { + defweight_prev_init(dvert_prev, me->dvert, index_mirr); + } + + oldw = BKE_defvert_multipaint_collective_weight( + dv_prev, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); + } + else { + oldw = curw; + } + + neww = wpaint_blend(wp, oldw, alpha, paintweight, wpi->brush_alpha_value, wpi->do_flip); + neww = wpaint_clamp_monotonic(oldw, curw, neww); + + change = neww / curw; + + /* verify for all groups that 0 < result <= 1 */ + multipaint_clamp_change(dv, wpi->defbase_tot, wpi->defbase_sel, &change); + + if (dv_mirr != NULL) { + curw_mirr = BKE_defvert_multipaint_collective_weight( + dv_mirr, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); + + if (curw_mirr == 0.0f) { + /* can't mirror into a zero weight vertex */ + dv_mirr = NULL; + } + else { + /* mirror is changed to achieve the same collective weight value */ + float orig = change_mirr = curw * change / curw_mirr; + + multipaint_clamp_change(dv_mirr, wpi->defbase_tot, wpi->defbase_sel, &change_mirr); + + if (!multipaint_verify_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel)) { + return; + } + + change *= change_mirr / orig; + } + } + + if (!multipaint_verify_change(dv, wpi->defbase_tot, change, wpi->defbase_sel)) { + return; + } + + /* apply validated change to vertex and mirror */ + multipaint_apply_change(dv, wpi->defbase_tot, change, wpi->defbase_sel); + + if (dv_mirr != NULL) { + multipaint_apply_change(dv_mirr, wpi->defbase_tot, change_mirr, wpi->defbase_sel); + } + + /* normalize */ + if (wpi->do_auto_normalize) { + do_weight_paint_normalize_all_locked_try_active( + dv, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); + + if (dv_mirr != NULL) { + do_weight_paint_normalize_all_locked_try_active( + dv_mirr, wpi->defbase_tot, wpi->vgroup_validmap, wpi->lock_flags, wpi->active.lock); + } + } } static void do_weight_paint_vertex( - /* vars which remain the same for every vert */ - const VPaint *wp, Object *ob, const WeightPaintInfo *wpi, - /* vars which change on each stroke */ - const uint index, float alpha, float paintweight) -{ - if (wpi->do_multipaint) { - do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight); - } - else { - do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight); - } + /* vars which remain the same for every vert */ + const VPaint *wp, + Object *ob, + const WeightPaintInfo *wpi, + /* vars which change on each stroke */ + const uint index, + float alpha, + float paintweight) +{ + if (wpi->do_multipaint) { + do_weight_paint_vertex_multi(wp, ob, wpi, index, alpha, paintweight); + } + else { + do_weight_paint_vertex_single(wp, ob, wpi, index, alpha, paintweight); + } } - /* Toggle operator for turning vertex paint mode on or off (copied from sculpt.c) */ -static void vertex_paint_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob, eObjectMode object_mode) +static void vertex_paint_init_session(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + eObjectMode object_mode) { - /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); + /* Create persistent sculpt mode data */ + BKE_sculpt_toolsettings_data_ensure(scene); - BLI_assert(ob->sculpt == NULL); - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); - ob->sculpt->mode_type = object_mode; - BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); + BLI_assert(ob->sculpt == NULL); + ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->mode_type = object_mode; + BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); } static void vertex_paint_init_stroke(Depsgraph *depsgraph, Scene *scene, Object *ob) { - BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); + BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); } static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) { - /* Create maps */ - struct SculptVertexPaintGeomMap *gmap = NULL; - if (ob->mode == OB_MODE_VERTEX_PAINT) { - gmap = &ob->sculpt->mode.vpaint.gmap; - BLI_assert(ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT); - } - else if (ob->mode == OB_MODE_WEIGHT_PAINT) { - gmap = &ob->sculpt->mode.wpaint.gmap; - BLI_assert(ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT); - } - else { - ob->sculpt->mode_type = 0; - BLI_assert(0); - return; - } - - Mesh *me = ob->data; - - if (gmap->vert_to_loop == NULL) { - gmap->vert_map_mem = NULL; - gmap->vert_to_loop = NULL; - gmap->poly_map_mem = NULL; - gmap->vert_to_poly = NULL; - BKE_mesh_vert_loop_map_create( - &gmap->vert_to_loop, - &gmap->vert_map_mem, - me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); - BKE_mesh_vert_poly_map_create( - &gmap->vert_to_poly, - &gmap->poly_map_mem, - me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop); - } - - /* Create average brush arrays */ - if (ob->mode == OB_MODE_VERTEX_PAINT) { - if (!brush_use_accumulate(ts->vpaint)) { - if (ob->sculpt->mode.vpaint.previous_color == NULL) { - ob->sculpt->mode.vpaint.previous_color = - MEM_callocN(me->totloop * sizeof(uint), __func__); - } - } - else { - MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color); - } - } - else if (ob->mode == OB_MODE_WEIGHT_PAINT) { - if (!brush_use_accumulate(ts->wpaint)) { - if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { - ob->sculpt->mode.wpaint.alpha_weight = - MEM_callocN(me->totvert * sizeof(float), __func__); - } - if (ob->sculpt->mode.wpaint.dvert_prev == NULL) { - ob->sculpt->mode.wpaint.dvert_prev = - MEM_callocN(me->totvert * sizeof(MDeformVert), __func__); - MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; - for (int i = 0; i < me->totvert; i++, dv++) { - /* Use to show this isn't initialized, never apply to the mesh data. */ - dv->flag = 1; - } - } - } - else { - MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight); - if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { - BKE_defvert_array_free_elems(ob->sculpt->mode.wpaint.dvert_prev, me->totvert); - MEM_freeN(ob->sculpt->mode.wpaint.dvert_prev); - ob->sculpt->mode.wpaint.dvert_prev = NULL; - } - } - } - + /* Create maps */ + struct SculptVertexPaintGeomMap *gmap = NULL; + if (ob->mode == OB_MODE_VERTEX_PAINT) { + gmap = &ob->sculpt->mode.vpaint.gmap; + BLI_assert(ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT); + } + else if (ob->mode == OB_MODE_WEIGHT_PAINT) { + gmap = &ob->sculpt->mode.wpaint.gmap; + BLI_assert(ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT); + } + else { + ob->sculpt->mode_type = 0; + BLI_assert(0); + return; + } + + Mesh *me = ob->data; + + if (gmap->vert_to_loop == NULL) { + gmap->vert_map_mem = NULL; + gmap->vert_to_loop = NULL; + gmap->poly_map_mem = NULL; + gmap->vert_to_poly = NULL; + BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, + &gmap->vert_map_mem, + me->mpoly, + me->mloop, + me->totvert, + me->totpoly, + me->totloop); + BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly, + &gmap->poly_map_mem, + me->mpoly, + me->mloop, + me->totvert, + me->totpoly, + me->totloop); + } + + /* Create average brush arrays */ + if (ob->mode == OB_MODE_VERTEX_PAINT) { + if (!brush_use_accumulate(ts->vpaint)) { + if (ob->sculpt->mode.vpaint.previous_color == NULL) { + ob->sculpt->mode.vpaint.previous_color = MEM_callocN(me->totloop * sizeof(uint), __func__); + } + } + else { + MEM_SAFE_FREE(ob->sculpt->mode.vpaint.previous_color); + } + } + else if (ob->mode == OB_MODE_WEIGHT_PAINT) { + if (!brush_use_accumulate(ts->wpaint)) { + if (ob->sculpt->mode.wpaint.alpha_weight == NULL) { + ob->sculpt->mode.wpaint.alpha_weight = MEM_callocN(me->totvert * sizeof(float), __func__); + } + if (ob->sculpt->mode.wpaint.dvert_prev == NULL) { + ob->sculpt->mode.wpaint.dvert_prev = MEM_callocN(me->totvert * sizeof(MDeformVert), + __func__); + MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; + for (int i = 0; i < me->totvert; i++, dv++) { + /* Use to show this isn't initialized, never apply to the mesh data. */ + dv->flag = 1; + } + } + } + else { + MEM_SAFE_FREE(ob->sculpt->mode.wpaint.alpha_weight); + if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { + BKE_defvert_array_free_elems(ob->sculpt->mode.wpaint.dvert_prev, me->totvert); + MEM_freeN(ob->sculpt->mode.wpaint.dvert_prev); + ob->sculpt->mode.wpaint.dvert_prev = NULL; + } + } + } } /* -------------------------------------------------------------------- */ /** \name Enter Vertex/Weight Paint Mode * \{ */ -static void ed_vwpaintmode_enter_generic( - Main *bmain, Depsgraph *depsgraph, - wmWindowManager *wm, Scene *scene, - Object *ob, const eObjectMode mode_flag) -{ - ob->mode |= mode_flag; - Mesh *me = BKE_mesh_from_object(ob); - - /* Same as sculpt mode, make sure we don't have cached derived mesh which - * points to freed arrays. - */ - BKE_object_free_derived_caches(ob); - - if (mode_flag == OB_MODE_VERTEX_PAINT) { - const ePaintMode paint_mode = PAINT_MODE_VERTEX; - ED_mesh_color_ensure(me, NULL); - - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - paint_cursor_start_explicit(paint, wm, vertex_paint_poll); - BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); - } - else if (mode_flag == OB_MODE_WEIGHT_PAINT) { - const ePaintMode paint_mode = PAINT_MODE_WEIGHT; - - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); - Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); - paint_cursor_start_explicit(paint, wm, weight_paint_poll); - BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); - - /* weight paint specific */ - ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 's'); - ED_vgroup_sync_from_pose(ob); - } - else { - BLI_assert(0); - } - - /* Create vertex/weight paint mode session data */ - if (ob->sculpt) { - if (ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } - BKE_sculptsession_free(ob); - } - - vertex_paint_init_session(depsgraph, scene, ob, mode_flag); - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); +static void ed_vwpaintmode_enter_generic(Main *bmain, + Depsgraph *depsgraph, + wmWindowManager *wm, + Scene *scene, + Object *ob, + const eObjectMode mode_flag) +{ + ob->mode |= mode_flag; + Mesh *me = BKE_mesh_from_object(ob); + + /* Same as sculpt mode, make sure we don't have cached derived mesh which + * points to freed arrays. + */ + BKE_object_free_derived_caches(ob); + + if (mode_flag == OB_MODE_VERTEX_PAINT) { + const ePaintMode paint_mode = PAINT_MODE_VERTEX; + ED_mesh_color_ensure(me, NULL); + + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint); + Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); + paint_cursor_start_explicit(paint, wm, vertex_paint_poll); + BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT); + } + else if (mode_flag == OB_MODE_WEIGHT_PAINT) { + const ePaintMode paint_mode = PAINT_MODE_WEIGHT; + + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint); + Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode); + paint_cursor_start_explicit(paint, wm, weight_paint_poll); + BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT); + + /* weight paint specific */ + ED_mesh_mirror_spatial_table(ob, NULL, NULL, NULL, 's'); + ED_vgroup_sync_from_pose(ob); + } + else { + BLI_assert(0); + } + + /* Create vertex/weight paint mode session data */ + if (ob->sculpt) { + if (ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } + BKE_sculptsession_free(ob); + } + + vertex_paint_init_session(depsgraph, scene, ob, mode_flag); + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_vpaintmode_enter_ex( - Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, - Scene *scene, Object *ob) + Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, Scene *scene, Object *ob) { - ed_vwpaintmode_enter_generic( - bmain, depsgraph, wm, scene, ob, OB_MODE_VERTEX_PAINT); + ed_vwpaintmode_enter_generic(bmain, depsgraph, wm, scene, ob, OB_MODE_VERTEX_PAINT); } void ED_object_vpaintmode_enter(struct bContext *C) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - wmWindowManager *wm = CTX_wm_manager(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); } void ED_object_wpaintmode_enter_ex( - Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, - Scene *scene, Object *ob) + Main *bmain, Depsgraph *depsgraph, wmWindowManager *wm, Scene *scene, Object *ob) { - ed_vwpaintmode_enter_generic( - bmain, depsgraph, wm, scene, ob, OB_MODE_WEIGHT_PAINT); + ed_vwpaintmode_enter_generic(bmain, depsgraph, wm, scene, ob, OB_MODE_WEIGHT_PAINT); } void ED_object_wpaintmode_enter(struct bContext *C) { - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - wmWindowManager *wm = CTX_wm_manager(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + wmWindowManager *wm = CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); } /** \} */ @@ -1153,72 +1185,71 @@ void ED_object_wpaintmode_enter(struct bContext *C) /** \name Exit Vertex/Weight Paint Mode * \{ */ -static void ed_vwpaintmode_exit_generic( - Object *ob, const eObjectMode mode_flag) -{ - Mesh *me = BKE_mesh_from_object(ob); - ob->mode &= ~mode_flag; - - if (mode_flag == OB_MODE_VERTEX_PAINT) { - if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { - BKE_mesh_flush_select_from_polys(me); - } - else if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { - BKE_mesh_flush_select_from_verts(me); - } - } - else if (mode_flag == OB_MODE_WEIGHT_PAINT) { - if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { - BKE_mesh_flush_select_from_verts(me); - } - else if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { - BKE_mesh_flush_select_from_polys(me); - } - } - else { - BLI_assert(0); - } - - /* If the cache is not released by a cancel or a done, free it now. */ - if (ob->sculpt && ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } - - BKE_sculptsession_free(ob); - - paint_cursor_delete_textures(); - - if (mode_flag == OB_MODE_WEIGHT_PAINT) { - ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e'); - ED_mesh_mirror_topo_table(NULL, NULL, 'e'); - } - - /* Never leave derived meshes behind. */ - BKE_object_free_derived_caches(ob); - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); +static void ed_vwpaintmode_exit_generic(Object *ob, const eObjectMode mode_flag) +{ + Mesh *me = BKE_mesh_from_object(ob); + ob->mode &= ~mode_flag; + + if (mode_flag == OB_MODE_VERTEX_PAINT) { + if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { + BKE_mesh_flush_select_from_polys(me); + } + else if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { + BKE_mesh_flush_select_from_verts(me); + } + } + else if (mode_flag == OB_MODE_WEIGHT_PAINT) { + if (me->editflag & ME_EDIT_PAINT_VERT_SEL) { + BKE_mesh_flush_select_from_verts(me); + } + else if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { + BKE_mesh_flush_select_from_polys(me); + } + } + else { + BLI_assert(0); + } + + /* If the cache is not released by a cancel or a done, free it now. */ + if (ob->sculpt && ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } + + BKE_sculptsession_free(ob); + + paint_cursor_delete_textures(); + + if (mode_flag == OB_MODE_WEIGHT_PAINT) { + ED_mesh_mirror_spatial_table(NULL, NULL, NULL, NULL, 'e'); + ED_mesh_mirror_topo_table(NULL, NULL, 'e'); + } + + /* Never leave derived meshes behind. */ + BKE_object_free_derived_caches(ob); + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_vpaintmode_exit_ex(Object *ob) { - ed_vwpaintmode_exit_generic(ob, OB_MODE_VERTEX_PAINT); + ed_vwpaintmode_exit_generic(ob, OB_MODE_VERTEX_PAINT); } void ED_object_vpaintmode_exit(struct bContext *C) { - Object *ob = CTX_data_active_object(C); - ED_object_vpaintmode_exit_ex(ob); + Object *ob = CTX_data_active_object(C); + ED_object_vpaintmode_exit_ex(ob); } void ED_object_wpaintmode_exit_ex(Object *ob) { - ed_vwpaintmode_exit_generic(ob, OB_MODE_WEIGHT_PAINT); + ed_vwpaintmode_exit_generic(ob, OB_MODE_WEIGHT_PAINT); } void ED_object_wpaintmode_exit(struct bContext *C) { - Object *ob = CTX_data_active_object(C); - ED_object_wpaintmode_exit_ex(ob); + Object *ob = CTX_data_active_object(C); + ED_object_wpaintmode_exit_ex(ob); } /** \} */ @@ -1230,1159 +1261,1182 @@ void ED_object_wpaintmode_exit(struct bContext *C) */ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Object *ob = CTX_data_active_object(C); - const int mode_flag = OB_MODE_WEIGHT_PAINT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } - - Mesh *me = BKE_mesh_from_object(ob); - - if (is_mode_set) { - ED_object_wpaintmode_exit_ex(ob); - } - else { - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - wmWindowManager *wm = CTX_wm_manager(C); - ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); - BKE_paint_toolslots_brush_validate(bmain, &ts->wpaint->paint); - } - - /* When locked, it's almost impossible to select the pose then the object to enter weight paint mode. - * In this case move our pose object in/out of pose mode. - * This is in fits with the convention of selecting multiple objects and entering a mode. */ - if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { - Object *ob_arm = modifiers_isDeformedByArmature(ob); - if (ob_arm && (ob_arm->base_flag & BASE_SELECTED)) { - if (ob_arm->mode & OB_MODE_POSE) { - ED_object_posemode_exit_ex(bmain, ob_arm); - } - else { - ED_object_posemode_enter_ex(bmain, ob_arm); - } - } - } - - /* Weightpaint works by overriding colors in mesh, - * so need to make sure we recalc on enter and - * exit (exit needs doing regardless because we - * should redeform). - */ - DEG_id_tag_update(&me->id, 0); - - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - - WM_toolsystem_update_from_context_view3d(C); - - return OPERATOR_FINISHED; + Main *bmain = CTX_data_main(C); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Object *ob = CTX_data_active_object(C); + const int mode_flag = OB_MODE_WEIGHT_PAINT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + Mesh *me = BKE_mesh_from_object(ob); + + if (is_mode_set) { + ED_object_wpaintmode_exit_ex(ob); + } + else { + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + wmWindowManager *wm = CTX_wm_manager(C); + ED_object_wpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + BKE_paint_toolslots_brush_validate(bmain, &ts->wpaint->paint); + } + + /* When locked, it's almost impossible to select the pose then the object to enter weight paint mode. + * In this case move our pose object in/out of pose mode. + * This is in fits with the convention of selecting multiple objects and entering a mode. */ + if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) { + Object *ob_arm = modifiers_isDeformedByArmature(ob); + if (ob_arm && (ob_arm->base_flag & BASE_SELECTED)) { + if (ob_arm->mode & OB_MODE_POSE) { + ED_object_posemode_exit_ex(bmain, ob_arm); + } + else { + ED_object_posemode_enter_ex(bmain, ob_arm); + } + } + } + + /* Weightpaint works by overriding colors in mesh, + * so need to make sure we recalc on enter and + * exit (exit needs doing regardless because we + * should redeform). + */ + DEG_id_tag_update(&me->id, 0); + + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + + WM_toolsystem_update_from_context_view3d(C); + + return OPERATOR_FINISHED; } /* for switching to/from mode */ static bool paint_poll_test(bContext *C) { - Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->type != OB_MESH) - return 0; - if (!ob->data || ID_IS_LINKED(ob->data)) - return 0; - if (CTX_data_edit_object(C)) - return 0; - return 1; + Object *ob = CTX_data_active_object(C); + if (ob == NULL || ob->type != OB_MESH) + return 0; + if (!ob->data || ID_IS_LINKED(ob->data)) + return 0; + if (CTX_data_edit_object(C)) + return 0; + return 1; } void PAINT_OT_weight_paint_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Weight Paint Mode"; - ot->idname = "PAINT_OT_weight_paint_toggle"; - ot->description = "Toggle weight paint mode in 3D view"; + /* identifiers */ + ot->name = "Weight Paint Mode"; + ot->idname = "PAINT_OT_weight_paint_toggle"; + ot->description = "Toggle weight paint mode in 3D view"; - /* api callbacks */ - ot->exec = wpaint_mode_toggle_exec; - ot->poll = paint_poll_test; + /* api callbacks */ + ot->exec = wpaint_mode_toggle_exec; + ot->poll = paint_poll_test; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } /* ************ weight paint operator ********** */ struct WPaintData { - ViewContext vc; - struct NormalAnglePrecalc normal_angle_precalc; + ViewContext vc; + struct NormalAnglePrecalc normal_angle_precalc; - struct WeightPaintGroupData active, mirror; + struct WeightPaintGroupData active, mirror; - /* variables for auto normalize */ - const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */ - const bool *lock_flags; + /* variables for auto normalize */ + const bool *vgroup_validmap; /* stores if vgroups tie to deforming bones or not */ + const bool *lock_flags; - /* variables for multipaint */ - const bool *defbase_sel; /* set of selected groups */ - int defbase_tot_sel; /* number of selected groups */ - bool do_multipaint; /* true if multipaint enabled and multiple groups selected */ + /* variables for multipaint */ + const bool *defbase_sel; /* set of selected groups */ + int defbase_tot_sel; /* number of selected groups */ + bool do_multipaint; /* true if multipaint enabled and multiple groups selected */ - int defbase_tot; + int defbase_tot; - /* original weight values for use in blur/smear */ - float *precomputed_weight; - bool precomputed_weight_ready; + /* original weight values for use in blur/smear */ + float *precomputed_weight; + bool precomputed_weight_ready; }; /* Initialize the stroke cache invariants from operator properties */ static void vwpaint_update_cache_invariants( - bContext *C, const VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2]) -{ - StrokeCache *cache; - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - const Brush *brush = vp->paint.brush; - ViewContext *vc = paint_stroke_view_context(op->customdata); - Object *ob = CTX_data_active_object(C); - float mat[3][3]; - float view_dir[3] = {0.0f, 0.0f, 1.0f}; - int mode; - - /* VW paint needs to allocate stroke cache before update is called. */ - if (!ss->cache) { - cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); - ss->cache = cache; - } - else { - cache = ss->cache; - } - - /* Initial mouse location */ - if (mouse) - copy_v2_v2(cache->initial_mouse, mouse); - else - zero_v2(cache->initial_mouse); - - mode = RNA_enum_get(op->ptr, "mode"); - cache->invert = mode == BRUSH_STROKE_INVERT; - cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; - /* not very nice, but with current events system implementation - * we can't handle brush appearance inversion hotkey separately (sergey) */ - if (cache->invert) ups->draw_inverted = true; - else ups->draw_inverted = false; - - copy_v2_v2(cache->mouse, cache->initial_mouse); - /* Truly temporary data that isn't stored in properties */ - cache->vc = vc; - cache->brush = brush; - cache->first_time = 1; - - /* cache projection matrix */ - ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); - - invert_m4_m4(ob->imat, ob->obmat); - copy_m3_m4(mat, cache->vc->rv3d->viewinv); - mul_m3_v3(mat, view_dir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, view_dir); - normalize_v3_v3(cache->true_view_normal, view_dir); - - copy_v3_v3(cache->view_normal, cache->true_view_normal); - cache->bstrength = BKE_brush_alpha_get(scene, brush); - cache->is_last_valid = false; + bContext *C, const VPaint *vp, SculptSession *ss, wmOperator *op, const float mouse[2]) +{ + StrokeCache *cache; + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + const Brush *brush = vp->paint.brush; + ViewContext *vc = paint_stroke_view_context(op->customdata); + Object *ob = CTX_data_active_object(C); + float mat[3][3]; + float view_dir[3] = {0.0f, 0.0f, 1.0f}; + int mode; + + /* VW paint needs to allocate stroke cache before update is called. */ + if (!ss->cache) { + cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); + ss->cache = cache; + } + else { + cache = ss->cache; + } + + /* Initial mouse location */ + if (mouse) + copy_v2_v2(cache->initial_mouse, mouse); + else + zero_v2(cache->initial_mouse); + + mode = RNA_enum_get(op->ptr, "mode"); + cache->invert = mode == BRUSH_STROKE_INVERT; + cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; + /* not very nice, but with current events system implementation + * we can't handle brush appearance inversion hotkey separately (sergey) */ + if (cache->invert) + ups->draw_inverted = true; + else + ups->draw_inverted = false; + + copy_v2_v2(cache->mouse, cache->initial_mouse); + /* Truly temporary data that isn't stored in properties */ + cache->vc = vc; + cache->brush = brush; + cache->first_time = 1; + + /* cache projection matrix */ + ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); + + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, cache->vc->rv3d->viewinv); + mul_m3_v3(mat, view_dir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, view_dir); + normalize_v3_v3(cache->true_view_normal, view_dir); + + copy_v3_v3(cache->view_normal, cache->true_view_normal); + cache->bstrength = BKE_brush_alpha_get(scene, brush); + cache->is_last_valid = false; } /* Initialize the stroke cache variants from operator properties */ static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, PointerRNA *ptr) { - Scene *scene = CTX_data_scene(C); - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - Brush *brush = BKE_paint_brush(&vp->paint); - - /* This effects the actual brush radius, so things farther away - * are compared with a larger radius and vice versa. */ - if (cache->first_time) { - RNA_float_get_array(ptr, "location", cache->true_location); - } - - RNA_float_get_array(ptr, "mouse", cache->mouse); - - /* XXX: Use pressure value from first brush step for brushes which don't - * support strokes (grab, thumb). They depends on initial state and - * brush coord/pressure/etc. - * It's more an events design issue, which doesn't split coordinate/pressure/angle - * changing events. We should avoid this after events system re-design */ - if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { - cache->pressure = RNA_float_get(ptr, "pressure"); - } - - /* Truly temporary data that isn't stored in properties */ - if (cache->first_time) { - if (!BKE_brush_use_locked_size(scene, brush)) { - cache->initial_radius = paint_calc_object_space_radius( - cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); - BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); - } - else { - cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); - } - } - - if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { - cache->radius = cache->initial_radius * cache->pressure; - } - else { - cache->radius = cache->initial_radius; - } - - cache->radius_squared = cache->radius * cache->radius; - - if (ss->pbvh) { - BKE_pbvh_update(ss->pbvh, PBVH_UpdateRedraw, NULL); - BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - } + Scene *scene = CTX_data_scene(C); + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + Brush *brush = BKE_paint_brush(&vp->paint); + + /* This effects the actual brush radius, so things farther away + * are compared with a larger radius and vice versa. */ + if (cache->first_time) { + RNA_float_get_array(ptr, "location", cache->true_location); + } + + RNA_float_get_array(ptr, "mouse", cache->mouse); + + /* XXX: Use pressure value from first brush step for brushes which don't + * support strokes (grab, thumb). They depends on initial state and + * brush coord/pressure/etc. + * It's more an events design issue, which doesn't split coordinate/pressure/angle + * changing events. We should avoid this after events system re-design */ + if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { + cache->pressure = RNA_float_get(ptr, "pressure"); + } + + /* Truly temporary data that isn't stored in properties */ + if (cache->first_time) { + if (!BKE_brush_use_locked_size(scene, brush)) { + cache->initial_radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); + } + else { + cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); + } + } + + if (BKE_brush_use_size_pressure(scene, brush) && + paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { + cache->radius = cache->initial_radius * cache->pressure; + } + else { + cache->radius = cache->initial_radius; + } + + cache->radius_squared = cache->radius * cache->radius; + + if (ss->pbvh) { + BKE_pbvh_update(ss->pbvh, PBVH_UpdateRedraw, NULL); + BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); + } } static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { - Scene *scene = CTX_data_scene(C); - struct PaintStroke *stroke = op->customdata; - ToolSettings *ts = scene->toolsettings; - Object *ob = CTX_data_active_object(C); - Mesh *me = BKE_mesh_from_object(ob); - struct WPaintData *wpd; - struct WPaintVGroupIndex vgroup_index; - int defbase_tot, defbase_tot_sel; - bool *defbase_sel; - SculptSession *ss = ob->sculpt; - VPaint *vp = CTX_data_tool_settings(C)->wpaint; - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) { - return false; - } - - { - /* check if we are attempting to paint onto a locked vertex group, - * and other options disallow it from doing anything useful */ - bDeformGroup *dg; - dg = BLI_findlink(&ob->defbase, vgroup_index.active); - if (dg->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); - return false; - } - if (vgroup_index.mirror != -1) { - dg = BLI_findlink(&ob->defbase, vgroup_index.mirror); - if (dg->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); - return false; - } - } - } - - /* check that multipaint groups are unlocked */ - defbase_tot = BLI_listbase_count(&ob->defbase); - defbase_sel = BKE_object_defgroup_selected_get(ob, defbase_tot, &defbase_tot_sel); - - if (ts->multipaint && defbase_tot_sel > 1) { - int i; - bDeformGroup *dg; - - if (me->editflag & ME_EDIT_MIRROR_X) { - BKE_object_defgroup_mirror_selection(ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel); - } - - for (i = 0; i < defbase_tot; i++) { - if (defbase_sel[i]) { - dg = BLI_findlink(&ob->defbase, i); - if (dg->flag & DG_LOCK_WEIGHT) { - BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); - MEM_freeN(defbase_sel); - return false; - } - } - } - } - - /* ALLOCATIONS! no return after this line */ - /* make mode data storage */ - wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); - paint_stroke_set_mode_data(stroke, wpd); - ED_view3d_viewcontext_init(C, &wpd->vc); - view_angle_limits_init( - &wpd->normal_angle_precalc, vp->paint.brush->falloff_angle, - (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); - - wpd->active.index = vgroup_index.active; - wpd->mirror.index = vgroup_index.mirror; - - /* multipaint */ - wpd->defbase_tot = defbase_tot; - wpd->defbase_sel = defbase_sel; - wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1; - wpd->do_multipaint = (ts->multipaint && defbase_tot_sel > 1); - - /* set up auto-normalize, and generate map for detecting which - * vgroups affect deform bones */ - wpd->lock_flags = BKE_object_defgroup_lock_flags_get(ob, wpd->defbase_tot); - if (ts->auto_normalize || ts->multipaint || wpd->lock_flags) { - wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(ob, wpd->defbase_tot); - } - - if (wpd->do_multipaint && ts->auto_normalize) { - bool *tmpflags; - tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__); - if (wpd->lock_flags) { - BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot); - } - else { - memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot); - } - wpd->active.lock = tmpflags; - } - else if (ts->auto_normalize) { - bool *tmpflags; - - tmpflags = wpd->lock_flags ? - MEM_dupallocN(wpd->lock_flags) : - MEM_callocN(sizeof(bool) * defbase_tot, __func__); - tmpflags[wpd->active.index] = true; - wpd->active.lock = tmpflags; - - tmpflags = wpd->lock_flags ? - MEM_dupallocN(wpd->lock_flags) : - MEM_callocN(sizeof(bool) * defbase_tot, __func__); - tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true; - wpd->mirror.lock = tmpflags; - } - - if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { - wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__); - } - - /* If not previously created, create vertex/weight paint mode session data */ - vertex_paint_init_stroke(depsgraph, scene, ob); - vwpaint_update_cache_invariants(C, vp, ss, op, mouse); - vertex_paint_init_session_data(ts, ob); - - if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { - MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; - for (int i = 0; i < me->totvert; i++, dv++) { - /* Use to show this isn't initialized, never apply to the mesh data. */ - dv->flag = 1; - } - } - - return true; + Scene *scene = CTX_data_scene(C); + struct PaintStroke *stroke = op->customdata; + ToolSettings *ts = scene->toolsettings; + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_mesh_from_object(ob); + struct WPaintData *wpd; + struct WPaintVGroupIndex vgroup_index; + int defbase_tot, defbase_tot_sel; + bool *defbase_sel; + SculptSession *ss = ob->sculpt; + VPaint *vp = CTX_data_tool_settings(C)->wpaint; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, &vgroup_index) == false) { + return false; + } + + { + /* check if we are attempting to paint onto a locked vertex group, + * and other options disallow it from doing anything useful */ + bDeformGroup *dg; + dg = BLI_findlink(&ob->defbase, vgroup_index.active); + if (dg->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); + return false; + } + if (vgroup_index.mirror != -1) { + dg = BLI_findlink(&ob->defbase, vgroup_index.mirror); + if (dg->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); + return false; + } + } + } + + /* check that multipaint groups are unlocked */ + defbase_tot = BLI_listbase_count(&ob->defbase); + defbase_sel = BKE_object_defgroup_selected_get(ob, defbase_tot, &defbase_tot_sel); + + if (ts->multipaint && defbase_tot_sel > 1) { + int i; + bDeformGroup *dg; + + if (me->editflag & ME_EDIT_MIRROR_X) { + BKE_object_defgroup_mirror_selection( + ob, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel); + } + + for (i = 0; i < defbase_tot; i++) { + if (defbase_sel[i]) { + dg = BLI_findlink(&ob->defbase, i); + if (dg->flag & DG_LOCK_WEIGHT) { + BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); + MEM_freeN(defbase_sel); + return false; + } + } + } + } + + /* ALLOCATIONS! no return after this line */ + /* make mode data storage */ + wpd = MEM_callocN(sizeof(struct WPaintData), "WPaintData"); + paint_stroke_set_mode_data(stroke, wpd); + ED_view3d_viewcontext_init(C, &wpd->vc); + view_angle_limits_init(&wpd->normal_angle_precalc, + vp->paint.brush->falloff_angle, + (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + + wpd->active.index = vgroup_index.active; + wpd->mirror.index = vgroup_index.mirror; + + /* multipaint */ + wpd->defbase_tot = defbase_tot; + wpd->defbase_sel = defbase_sel; + wpd->defbase_tot_sel = defbase_tot_sel > 1 ? defbase_tot_sel : 1; + wpd->do_multipaint = (ts->multipaint && defbase_tot_sel > 1); + + /* set up auto-normalize, and generate map for detecting which + * vgroups affect deform bones */ + wpd->lock_flags = BKE_object_defgroup_lock_flags_get(ob, wpd->defbase_tot); + if (ts->auto_normalize || ts->multipaint || wpd->lock_flags) { + wpd->vgroup_validmap = BKE_object_defgroup_validmap_get(ob, wpd->defbase_tot); + } + + if (wpd->do_multipaint && ts->auto_normalize) { + bool *tmpflags; + tmpflags = MEM_mallocN(sizeof(bool) * defbase_tot, __func__); + if (wpd->lock_flags) { + BLI_array_binary_or(tmpflags, wpd->defbase_sel, wpd->lock_flags, wpd->defbase_tot); + } + else { + memcpy(tmpflags, wpd->defbase_sel, sizeof(*tmpflags) * wpd->defbase_tot); + } + wpd->active.lock = tmpflags; + } + else if (ts->auto_normalize) { + bool *tmpflags; + + tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) : + MEM_callocN(sizeof(bool) * defbase_tot, __func__); + tmpflags[wpd->active.index] = true; + wpd->active.lock = tmpflags; + + tmpflags = wpd->lock_flags ? MEM_dupallocN(wpd->lock_flags) : + MEM_callocN(sizeof(bool) * defbase_tot, __func__); + tmpflags[(wpd->mirror.index != -1) ? wpd->mirror.index : wpd->active.index] = true; + wpd->mirror.lock = tmpflags; + } + + if (ELEM(vp->paint.brush->weightpaint_tool, WPAINT_TOOL_SMEAR, WPAINT_TOOL_BLUR)) { + wpd->precomputed_weight = MEM_mallocN(sizeof(float) * me->totvert, __func__); + } + + /* If not previously created, create vertex/weight paint mode session data */ + vertex_paint_init_stroke(depsgraph, scene, ob); + vwpaint_update_cache_invariants(C, vp, ss, op, mouse); + vertex_paint_init_session_data(ts, ob); + + if (ob->sculpt->mode.wpaint.dvert_prev != NULL) { + MDeformVert *dv = ob->sculpt->mode.wpaint.dvert_prev; + for (int i = 0; i < me->totvert; i++, dv++) { + /* Use to show this isn't initialized, never apply to the mesh data. */ + dv->flag = 1; + } + } + + return true; } static float dot_vf3vs3(const float brushNormal[3], const short vertexNormal[3]) { - float normal[3]; - normal_short_to_float_v3(normal, vertexNormal); - return dot_v3v3(brushNormal, normal); + float normal[3]; + normal_short_to_float_v3(normal, vertexNormal); + return dot_v3v3(brushNormal, normal); } -static void get_brush_alpha_data( - const Scene *scene, const SculptSession *ss, const Brush *brush, - float *r_brush_size_pressure, float *r_brush_alpha_value, float *r_brush_alpha_pressure) +static void get_brush_alpha_data(const Scene *scene, + const SculptSession *ss, + const Brush *brush, + float *r_brush_size_pressure, + float *r_brush_alpha_value, + float *r_brush_alpha_pressure) { - *r_brush_size_pressure = - BKE_brush_size_get(scene, brush) * - (BKE_brush_use_size_pressure(scene, brush) ? ss->cache->pressure : 1.0f); - *r_brush_alpha_value = - BKE_brush_alpha_get(scene, brush); - *r_brush_alpha_pressure = - (BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : 1.0f); + *r_brush_size_pressure = BKE_brush_size_get(scene, brush) * + (BKE_brush_use_size_pressure(scene, brush) ? ss->cache->pressure : + 1.0f); + *r_brush_alpha_value = BKE_brush_alpha_get(scene, brush); + *r_brush_alpha_pressure = (BKE_brush_use_alpha_pressure(scene, brush) ? ss->cache->pressure : + 1.0f); } static float wpaint_get_active_weight(const MDeformVert *dv, const WeightPaintInfo *wpi) { - if (wpi->do_multipaint) { - float weight = BKE_defvert_multipaint_collective_weight( - dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); + if (wpi->do_multipaint) { + float weight = BKE_defvert_multipaint_collective_weight( + dv, wpi->defbase_tot, wpi->defbase_sel, wpi->defbase_tot_sel, wpi->do_auto_normalize); - CLAMP(weight, 0.0f, 1.0f); - return weight; - } - else { - return defvert_find_weight(dv, wpi->active.index); - } + CLAMP(weight, 0.0f, 1.0f); + return weight; + } + else { + return defvert_find_weight(dv, wpi->active.index); + } } -static void do_wpaint_precompute_weight_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - const MDeformVert *dv = &data->me->dvert[n]; + SculptThreadedTaskData *data = userdata; + const MDeformVert *dv = &data->me->dvert[n]; - data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi); + data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi); } static void precompute_weight_values( - bContext *C, Object *ob, Brush *brush, struct WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) -{ - if (wpd->precomputed_weight_ready && !brush_use_accumulate_ex(brush, ob->mode)) - return; - - /* threaded loop over vertices */ - SculptThreadedTaskData data = { - .C = C, .ob = ob, .wpd = wpd, .wpi = wpi, .me = me, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range( - 0, me->totvert, - &data, - do_wpaint_precompute_weight_cb_ex, - &settings); - - wpd->precomputed_weight_ready = true; -} - -static void do_wpaint_brush_blur_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; - - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - Scene *scene = CTX_data_scene(data->C); - - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const char v_flag = data->me->mvert[v_index].flag; - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { - /* Get the average poly weight */ - int total_hit_loops = 0; - float weight_final = 0.0f; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - - total_hit_loops += mp->totloop; - for (int k = 0; k < mp->totloop; k++) { - const int l_index = mp->loopstart + k; - const MLoop *ml = &data->me->mloop[l_index]; - weight_final += data->wpd->precomputed_weight[ml->v]; - } - } - - /* Apply the weight to the vertex. */ - if (total_hit_loops != 0) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - const float final_alpha = - brush_fade * brush_strength * - grid_alpha * brush_alpha_pressure; - - if ((brush->flag & BRUSH_ACCUMULATE) == 0) { - if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { - ss->mode.wpaint.alpha_weight[v_index] = final_alpha; - } - else { - continue; - } - } - - weight_final /= total_hit_loops; - /* Only paint visible verts */ - do_weight_paint_vertex( - data->vp, data->ob, data->wpi, - v_index, final_alpha, weight_final); - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_wpaint_brush_smear_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; - - const Brush *brush = data->brush; - const Scene *scene = CTX_data_scene(data->C); - const StrokeCache *cache = ss->cache; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - float brush_dir[3]; - - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); - - if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv_curr = &data->me->mvert[v_index]; - - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - bool do_color = false; - /* Minimum dot product between brush direction and current - * to neighbor direction is 0.0, meaning orthogonal. */ - float stroke_dot_max = 0.0f; - - /* Get the color of the loop in the opposite direction of the brush movement - * (this callback is specifically for smear.) */ - float weight_final = 0.0; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - const MLoop *ml_other = &data->me->mloop[mp->loopstart]; - for (int k = 0; k < mp->totloop; k++, ml_other++) { - const uint v_other_index = ml_other->v; - if (v_other_index != v_index) { - const MVert *mv_other = &data->me->mvert[v_other_index]; - - /* Get the direction from the selected vert to the neighbor. */ - float other_dir[3]; - sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); - - normalize_v3(other_dir); - - const float stroke_dot = dot_v3v3(other_dir, brush_dir); - - if (stroke_dot > stroke_dot_max) { - stroke_dot_max = stroke_dot; - weight_final = data->wpd->precomputed_weight[v_other_index]; - do_color = true; - } - } - } - } - /* Apply weight to vertex */ - if (do_color) { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - const float final_alpha = - brush_fade * brush_strength * - grid_alpha * brush_alpha_pressure; - - if (final_alpha <= 0.0f) - continue; - - do_weight_paint_vertex( - data->vp, data->ob, data->wpi, - v_index, final_alpha, (float)weight_final); - } - } - } - } - } - BKE_pbvh_vertex_iter_end; - } -} - -static void do_wpaint_brush_draw_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const Scene *scene = CTX_data_scene(data->C); - - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - /* note: normally `BKE_brush_weight_get(scene, brush)` is used, - * however in this case we calculate a new weight each time. */ - const float paintweight = data->strength; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* Note: grids are 1:1 with corners (aka loops). - * For multires, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - - const char v_flag = data->me->mvert[v_index].flag; - /* If the vertex is selected */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - const float final_alpha = brush_fade * brush_strength * grid_alpha * brush_alpha_pressure; - - if ((brush->flag & BRUSH_ACCUMULATE) == 0) { - if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { - ss->mode.wpaint.alpha_weight[v_index] = final_alpha; - } - else { - continue; - } - } - - do_weight_paint_vertex( - data->vp, data->ob, data->wpi, - v_index, final_alpha, paintweight); - } - } - } - } - BKE_pbvh_vertex_iter_end; + bContext *C, Object *ob, Brush *brush, struct WPaintData *wpd, WeightPaintInfo *wpi, Mesh *me) +{ + if (wpd->precomputed_weight_ready && !brush_use_accumulate_ex(brush, ob->mode)) + return; + + /* threaded loop over vertices */ + SculptThreadedTaskData data = { + .C = C, + .ob = ob, + .wpd = wpd, + .wpi = wpi, + .me = me, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, me->totvert, &data, do_wpaint_precompute_weight_cb_ex, &settings); + + wpd->precomputed_weight_ready = true; +} + +static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; + + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + Scene *scene = CTX_data_scene(data->C); + + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const char v_flag = data->me->mvert[v_index].flag; + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + /* Get the average poly weight */ + int total_hit_loops = 0; + float weight_final = 0.0f; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &data->me->mpoly[p_index]; + + total_hit_loops += mp->totloop; + for (int k = 0; k < mp->totloop; k++) { + const int l_index = mp->loopstart + k; + const MLoop *ml = &data->me->mloop[l_index]; + weight_final += data->wpd->precomputed_weight[ml->v]; + } + } + + /* Apply the weight to the vertex. */ + if (total_hit_loops != 0) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + const float final_alpha = brush_fade * brush_strength * grid_alpha * + brush_alpha_pressure; + + if ((brush->flag & BRUSH_ACCUMULATE) == 0) { + if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { + ss->mode.wpaint.alpha_weight[v_index] = final_alpha; + } + else { + continue; + } + } + + weight_final /= total_hit_loops; + /* Only paint visible verts */ + do_weight_paint_vertex( + data->vp, data->ob, data->wpi, v_index, final_alpha, weight_final); + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.wpaint.gmap; + + const Brush *brush = data->brush; + const Scene *scene = CTX_data_scene(data->C); + const StrokeCache *cache = ss->cache; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + float brush_dir[3]; + + sub_v3_v3v3(brush_dir, cache->location, cache->last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + + if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv_curr = &data->me->mvert[v_index]; + + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) { + bool do_color = false; + /* Minimum dot product between brush direction and current + * to neighbor direction is 0.0, meaning orthogonal. */ + float stroke_dot_max = 0.0f; + + /* Get the color of the loop in the opposite direction of the brush movement + * (this callback is specifically for smear.) */ + float weight_final = 0.0; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &data->me->mpoly[p_index]; + const MLoop *ml_other = &data->me->mloop[mp->loopstart]; + for (int k = 0; k < mp->totloop; k++, ml_other++) { + const uint v_other_index = ml_other->v; + if (v_other_index != v_index) { + const MVert *mv_other = &data->me->mvert[v_other_index]; + + /* Get the direction from the selected vert to the neighbor. */ + float other_dir[3]; + sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); + project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + + normalize_v3(other_dir); + + const float stroke_dot = dot_v3v3(other_dir, brush_dir); + + if (stroke_dot > stroke_dot_max) { + stroke_dot_max = stroke_dot; + weight_final = data->wpd->precomputed_weight[v_other_index]; + do_color = true; + } + } + } + } + /* Apply weight to vertex */ + if (do_color) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + const float final_alpha = brush_fade * brush_strength * grid_alpha * + brush_alpha_pressure; + + if (final_alpha <= 0.0f) + continue; + + do_weight_paint_vertex( + data->vp, data->ob, data->wpi, v_index, final_alpha, (float)weight_final); + } + } + } + } + } + BKE_pbvh_vertex_iter_end; + } +} + +static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const Scene *scene = CTX_data_scene(data->C); + + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + /* note: normally `BKE_brush_weight_get(scene, brush)` is used, + * however in this case we calculate a new weight each time. */ + const float paintweight = data->strength; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* Note: grids are 1:1 with corners (aka loops). + * For multires, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + + const char v_flag = data->me->mvert[v_index].flag; + /* If the vertex is selected */ + if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->wpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + const float final_alpha = brush_fade * brush_strength * grid_alpha * + brush_alpha_pressure; + + if ((brush->flag & BRUSH_ACCUMULATE) == 0) { + if (ss->mode.wpaint.alpha_weight[v_index] < final_alpha) { + ss->mode.wpaint.alpha_weight[v_index] = final_alpha; + } + else { + continue; + } + } + + do_weight_paint_vertex(data->vp, data->ob, data->wpi, v_index, final_alpha, paintweight); + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_wpaint_brush_calc_average_weight_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - StrokeCache *cache = ss->cache; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - - struct WPaintAverageAccum *accum = (struct WPaintAverageAccum *)data->custom_data + n; - accum->len = 0; - accum->value = 0.0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - // const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const char v_flag = data->me->mvert[v_index].flag; - - /* If the vertex is selected. */ - if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { - const MDeformVert *dv = &data->me->dvert[v_index]; - accum->len += 1; - accum->value += wpaint_get_active_weight(dv, data->wpi); - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void calculate_average_weight(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode) -{ - struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); - data->custom_data = accum; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((data->sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - data, - do_wpaint_brush_calc_average_weight_cb_ex, - &settings); - - uint accum_len = 0; - double accum_weight = 0.0; - for (int i = 0; i < totnode; i++) { - accum_len += accum[i].len; - accum_weight += accum[i].value; - } - if (accum_len != 0) { - accum_weight /= accum_len; - data->strength = (float)accum_weight; - } - - MEM_SAFE_FREE(data->custom_data); /* 'accum' */ -} - - -static void wpaint_paint_leaves( - bContext *C, Object *ob, Sculpt *sd, VPaint *vp, struct WPaintData *wpd, WeightPaintInfo *wpi, - Mesh *me, PBVHNode **nodes, int totnode) -{ - Scene *scene = CTX_data_scene(C); - const Brush *brush = ob->sculpt->cache->brush; - - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .C = C, .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .vp = vp, .wpd = wpd, .wpi = wpi, .me = me, - }; - - /* Use this so average can modify its weight without touching the brush. */ - data.strength = BKE_brush_weight_get(scene, brush); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - /* NOTE: current mirroring code cannot be run in parallel */ - settings.use_threading = !(me->editflag & ME_EDIT_MIRROR_X); - - switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { - case WPAINT_TOOL_AVERAGE: - calculate_average_weight(&data, nodes, totnode); - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_draw_task_cb_ex, - &settings); - break; - case WPAINT_TOOL_SMEAR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_smear_task_cb_ex, - &settings); - break; - case WPAINT_TOOL_BLUR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_blur_task_cb_ex, - &settings); - break; - case WPAINT_TOOL_DRAW: - BLI_task_parallel_range( - 0, totnode, - &data, - do_wpaint_brush_draw_task_cb_ex, - &settings); - break; - } + void *__restrict userdata, const int n, const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + StrokeCache *cache = ss->cache; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + struct WPaintAverageAccum *accum = (struct WPaintAverageAccum *)data->custom_data + n; + accum->len = 0; + accum->value = 0.0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float angle_cos = (use_normal && vd.no) ? dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (angle_cos > 0.0 && + BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + // const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const char v_flag = data->me->mvert[v_index].flag; + + /* If the vertex is selected. */ + if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) { + const MDeformVert *dv = &data->me->dvert[v_index]; + accum->len += 1; + accum->value += wpaint_get_active_weight(dv, data->wpi); + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void calculate_average_weight(SculptThreadedTaskData *data, + PBVHNode **UNUSED(nodes), + int totnode) +{ + struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); + data->custom_data = accum; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((data->sd->flags & SCULPT_USE_OPENMP) && + totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); + + uint accum_len = 0; + double accum_weight = 0.0; + for (int i = 0; i < totnode; i++) { + accum_len += accum[i].len; + accum_weight += accum[i].value; + } + if (accum_len != 0) { + accum_weight /= accum_len; + data->strength = (float)accum_weight; + } + + MEM_SAFE_FREE(data->custom_data); /* 'accum' */ +} + +static void wpaint_paint_leaves(bContext *C, + Object *ob, + Sculpt *sd, + VPaint *vp, + struct WPaintData *wpd, + WeightPaintInfo *wpi, + Mesh *me, + PBVHNode **nodes, + int totnode) +{ + Scene *scene = CTX_data_scene(C); + const Brush *brush = ob->sculpt->cache->brush; + + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .C = C, + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .vp = vp, + .wpd = wpd, + .wpi = wpi, + .me = me, + }; + + /* Use this so average can modify its weight without touching the brush. */ + data.strength = BKE_brush_weight_get(scene, brush); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + /* NOTE: current mirroring code cannot be run in parallel */ + settings.use_threading = !(me->editflag & ME_EDIT_MIRROR_X); + + switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { + case WPAINT_TOOL_AVERAGE: + calculate_average_weight(&data, nodes, totnode); + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + break; + case WPAINT_TOOL_SMEAR: + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_smear_task_cb_ex, &settings); + break; + case WPAINT_TOOL_BLUR: + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_blur_task_cb_ex, &settings); + break; + case WPAINT_TOOL_DRAW: + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + break; + } } static PBVHNode **vwpaint_pbvh_gather_generic( - Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode) -{ - SculptSession *ss = ob->sculpt; - const bool use_normal = vwpaint_use_normal(wp); - PBVHNode **nodes = NULL; - - /* Build a list of all nodes that are potentially within the brush's area of influence */ - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = ss->cache->radius_squared, - .original = true, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); - if (use_normal) { - sculpt_pbvh_calc_area_normal(brush, ob, nodes, *r_totnode, true, ss->cache->sculpt_normal_symm); - } - else { - zero_v3(ss->cache->sculpt_normal_symm); - } - } - else { - struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; - dist_squared_ray_to_aabb_v3_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); - SculptSearchCircleData data = { - .ss = ss, - .sd = sd, - .radius_squared = ss->cache->radius_squared, - .original = true, - .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); - if (use_normal) { - copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal); - } - else { - zero_v3(ss->cache->sculpt_normal_symm); - } - } - return nodes; -} - -static void wpaint_do_paint( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi, - Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle) -{ - SculptSession *ss = ob->sculpt; - ss->cache->radial_symmetry_pass = i; - sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - - int totnode; - PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, wp, sd, brush, &totnode); - - wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode); - - if (nodes) - MEM_freeN(nodes); -} - -static void wpaint_do_radial_symmetry( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi, - Mesh *me, Brush *brush, const char symm, const int axis) -{ - for (int i = 1; i < wp->radial_symm[axis - 'X']; i++) { - const float angle = (2.0 * M_PI) * i / wp->radial_symm[axis - 'X']; - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, symm, axis, i, angle); - } + Object *ob, VPaint *wp, Sculpt *sd, Brush *brush, int *r_totnode) +{ + SculptSession *ss = ob->sculpt; + const bool use_normal = vwpaint_use_normal(wp); + PBVHNode **nodes = NULL; + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = ss->cache->radius_squared, + .original = true, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); + if (use_normal) { + sculpt_pbvh_calc_area_normal( + brush, ob, nodes, *r_totnode, true, ss->cache->sculpt_normal_symm); + } + else { + zero_v3(ss->cache->sculpt_normal_symm); + } + } + else { + struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; + dist_squared_ray_to_aabb_v3_precalc( + &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); + SculptSearchCircleData data = { + .ss = ss, + .sd = sd, + .radius_squared = ss->cache->radius_squared, + .original = true, + .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); + if (use_normal) { + copy_v3_v3(ss->cache->sculpt_normal_symm, ss->cache->view_normal); + } + else { + zero_v3(ss->cache->sculpt_normal_symm); + } + } + return nodes; +} + +static void wpaint_do_paint(bContext *C, + Object *ob, + VPaint *wp, + Sculpt *sd, + struct WPaintData *wpd, + WeightPaintInfo *wpi, + Mesh *me, + Brush *brush, + const char symm, + const int axis, + const int i, + const float angle) +{ + SculptSession *ss = ob->sculpt; + ss->cache->radial_symmetry_pass = i; + sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); + + int totnode; + PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, wp, sd, brush, &totnode); + + wpaint_paint_leaves(C, ob, sd, wp, wpd, wpi, me, nodes, totnode); + + if (nodes) + MEM_freeN(nodes); +} + +static void wpaint_do_radial_symmetry(bContext *C, + Object *ob, + VPaint *wp, + Sculpt *sd, + struct WPaintData *wpd, + WeightPaintInfo *wpi, + Mesh *me, + Brush *brush, + const char symm, + const int axis) +{ + for (int i = 1; i < wp->radial_symm[axis - 'X']; i++) { + const float angle = (2.0 * M_PI) * i / wp->radial_symm[axis - 'X']; + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, symm, axis, i, angle); + } } /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */ static void wpaint_do_symmetrical_brush_actions( - bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi) -{ - Brush *brush = BKE_paint_brush(&wp->paint); - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const char symm = wp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - int i = 0; - - /* initial stroke */ - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X'); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Y'); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Z'); - - cache->symmetry = symm; - - /* symm is a bit combination of XYZ - 1 is mirror - * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 1; i <= symm; i++) { - if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - cache->mirror_symmetry_pass = i; - cache->radial_symmetry_pass = 0; - sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); - - if (i & (1 << 0)) { - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X'); - } - if (i & (1 << 1)) { - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y'); - } - if (i & (1 << 2)) { - wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z', 0, 0); - wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z'); - } - } - } - copy_v3_v3(cache->true_last_location, cache->true_location); - cache->is_last_valid = true; + bContext *C, Object *ob, VPaint *wp, Sculpt *sd, struct WPaintData *wpd, WeightPaintInfo *wpi) +{ + Brush *brush = BKE_paint_brush(&wp->paint); + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const char symm = wp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + int i = 0; + + /* initial stroke */ + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'X'); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Y'); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, 0, 'Z'); + + cache->symmetry = symm; + + /* symm is a bit combination of XYZ - 1 is mirror + * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for (i = 1; i <= symm; i++) { + if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + cache->mirror_symmetry_pass = i; + cache->radial_symmetry_pass = 0; + sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); + + if (i & (1 << 0)) { + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'X'); + } + if (i & (1 << 1)) { + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Y'); + } + if (i & (1 << 2)) { + wpaint_do_paint(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z', 0, 0); + wpaint_do_radial_symmetry(C, ob, wp, sd, wpd, wpi, me, brush, i, 'Z'); + } + } + } + copy_v3_v3(cache->true_last_location, cache->true_location); + cache->is_last_valid = true; } static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - VPaint *wp = ts->wpaint; - Brush *brush = BKE_paint_brush(&wp->paint); - struct WPaintData *wpd = paint_stroke_mode_data(stroke); - ViewContext *vc; - Object *ob = CTX_data_active_object(C); - - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + VPaint *wp = ts->wpaint; + Brush *brush = BKE_paint_brush(&wp->paint); + struct WPaintData *wpd = paint_stroke_mode_data(stroke); + ViewContext *vc; + Object *ob = CTX_data_active_object(C); - vwpaint_update_cache_variants(C, wp, ob, itemptr); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - float mat[4][4]; - float mval[2]; + vwpaint_update_cache_variants(C, wp, ob, itemptr); - const float brush_alpha_value = BKE_brush_alpha_get(scene, brush); + float mat[4][4]; + float mval[2]; - /* intentionally don't initialize as NULL, make sure we initialize all members below */ - WeightPaintInfo wpi; + const float brush_alpha_value = BKE_brush_alpha_get(scene, brush); - /* cannot paint if there is no stroke data */ - if (wpd == NULL) { - /* XXX: force a redraw here, since even though we can't paint, - * at least view won't freeze until stroke ends */ - ED_region_tag_redraw(CTX_wm_region(C)); - return; - } + /* intentionally don't initialize as NULL, make sure we initialize all members below */ + WeightPaintInfo wpi; - vc = &wpd->vc; - ob = vc->obact; + /* cannot paint if there is no stroke data */ + if (wpd == NULL) { + /* XXX: force a redraw here, since even though we can't paint, + * at least view won't freeze until stroke ends */ + ED_region_tag_redraw(CTX_wm_region(C)); + return; + } - view3d_operator_needs_opengl(C); - ED_view3d_init_mats_rv3d(ob, vc->rv3d); + vc = &wpd->vc; + ob = vc->obact; - /* load projection matrix */ - mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + view3d_operator_needs_opengl(C); + ED_view3d_init_mats_rv3d(ob, vc->rv3d); + /* load projection matrix */ + mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); - /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */ - wpi.defbase_tot = wpd->defbase_tot; - wpi.defbase_sel = wpd->defbase_sel; - wpi.defbase_tot_sel = wpd->defbase_tot_sel; + /* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */ + wpi.defbase_tot = wpd->defbase_tot; + wpi.defbase_sel = wpd->defbase_sel; + wpi.defbase_tot_sel = wpd->defbase_tot_sel; - wpi.defbase_tot_unsel = wpi.defbase_tot - wpi.defbase_tot_sel; - wpi.active = wpd->active; - wpi.mirror = wpd->mirror; - wpi.lock_flags = wpd->lock_flags; - wpi.vgroup_validmap = wpd->vgroup_validmap; - wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip"); - wpi.do_multipaint = wpd->do_multipaint; - wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL)); - wpi.brush_alpha_value = brush_alpha_value; - /* *** done setting up WeightPaintInfo *** */ + wpi.defbase_tot_unsel = wpi.defbase_tot - wpi.defbase_tot_sel; + wpi.active = wpd->active; + wpi.mirror = wpd->mirror; + wpi.lock_flags = wpd->lock_flags; + wpi.vgroup_validmap = wpd->vgroup_validmap; + wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip"); + wpi.do_multipaint = wpd->do_multipaint; + wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL)); + wpi.brush_alpha_value = brush_alpha_value; + /* *** done setting up WeightPaintInfo *** */ - if (wpd->precomputed_weight) { - precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data); - } + if (wpd->precomputed_weight) { + precompute_weight_values(C, ob, brush, wpd, &wpi, ob->data); + } - wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); + wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi); - swap_m4m4(vc->rv3d->persmat, mat); + swap_m4m4(vc->rv3d->persmat, mat); - /* calculate pivot for rotation around seletion if needed */ - /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, mval); + /* calculate pivot for rotation around seletion if needed */ + /* also needed for "View Selected" on last stroke */ + paint_last_stroke_update(scene, vc->ar, mval); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - DEG_id_tag_update(ob->data, 0); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - swap_m4m4(wpd->vc.rv3d->persmat, mat); + DEG_id_tag_update(ob->data, 0); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + swap_m4m4(wpd->vc.rv3d->persmat, mat); - rcti r; - if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) { - if (ss->cache) { - ss->cache->current_r = r; - } + rcti r; + if (sculpt_get_redraw_rect(vc->ar, CTX_wm_region_view3d(C), ob, &r)) { + if (ss->cache) { + ss->cache->current_r = r; + } - /* previous is not set in the current cache else - * the partial rect will always grow */ - if (ss->cache) { - if (!BLI_rcti_is_empty(&ss->cache->previous_r)) - BLI_rcti_union(&r, &ss->cache->previous_r); - } + /* previous is not set in the current cache else + * the partial rect will always grow */ + if (ss->cache) { + if (!BLI_rcti_is_empty(&ss->cache->previous_r)) + BLI_rcti_union(&r, &ss->cache->previous_r); + } - r.xmin += vc->ar->winrct.xmin - 2; - r.xmax += vc->ar->winrct.xmin + 2; - r.ymin += vc->ar->winrct.ymin - 2; - r.ymax += vc->ar->winrct.ymin + 2; + r.xmin += vc->ar->winrct.xmin - 2; + r.xmax += vc->ar->winrct.xmin + 2; + r.ymin += vc->ar->winrct.ymin - 2; + r.ymax += vc->ar->winrct.ymin + 2; - ss->partial_redraw = 1; - } - ED_region_tag_redraw_partial(vc->ar, &r); + ss->partial_redraw = 1; + } + ED_region_tag_redraw_partial(vc->ar, &r); } static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) { - Object *ob = CTX_data_active_object(C); - struct WPaintData *wpd = paint_stroke_mode_data(stroke); - - if (wpd) { - if (wpd->defbase_sel) - MEM_freeN((void *)wpd->defbase_sel); - if (wpd->vgroup_validmap) - MEM_freeN((void *)wpd->vgroup_validmap); - if (wpd->lock_flags) - MEM_freeN((void *)wpd->lock_flags); - if (wpd->active.lock) - MEM_freeN((void *)wpd->active.lock); - if (wpd->mirror.lock) - MEM_freeN((void *)wpd->mirror.lock); - if (wpd->precomputed_weight) - MEM_freeN(wpd->precomputed_weight); - - MEM_freeN(wpd); - } - - /* and particles too */ - if (ob->particlesystem.first) { - ParticleSystem *psys; - int i; - - for (psys = ob->particlesystem.first; psys; psys = psys->next) { - for (i = 0; i < PSYS_TOT_VG; i++) { - if (psys->vgroup[i] == ob->actdef) { - psys->recalc |= ID_RECALC_PSYS_RESET; - break; - } - } - } - } - - DEG_id_tag_update(ob->data, 0); - - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + Object *ob = CTX_data_active_object(C); + struct WPaintData *wpd = paint_stroke_mode_data(stroke); + + if (wpd) { + if (wpd->defbase_sel) + MEM_freeN((void *)wpd->defbase_sel); + if (wpd->vgroup_validmap) + MEM_freeN((void *)wpd->vgroup_validmap); + if (wpd->lock_flags) + MEM_freeN((void *)wpd->lock_flags); + if (wpd->active.lock) + MEM_freeN((void *)wpd->active.lock); + if (wpd->mirror.lock) + MEM_freeN((void *)wpd->mirror.lock); + if (wpd->precomputed_weight) + MEM_freeN(wpd->precomputed_weight); + + MEM_freeN(wpd); + } + + /* and particles too */ + if (ob->particlesystem.first) { + ParticleSystem *psys; + int i; + + for (psys = ob->particlesystem.first; psys; psys = psys->next) { + for (i = 0; i < PSYS_TOT_VG; i++) { + if (psys->vgroup[i] == ob->actdef) { + psys->recalc |= ID_RECALC_PSYS_RESET; + break; + } + } + } + } + + DEG_id_tag_update(ob->data, 0); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; } - static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int retval; + int retval; - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, wpaint_stroke_test_start, - wpaint_stroke_update_step, NULL, - wpaint_stroke_done, event->type); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + wpaint_stroke_test_start, + wpaint_stroke_update_step, + NULL, + wpaint_stroke_done, + event->type); - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int wpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, wpaint_stroke_test_start, - wpaint_stroke_update_step, NULL, - wpaint_stroke_done, 0); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + wpaint_stroke_test_start, + wpaint_stroke_update_step, + NULL, + wpaint_stroke_done, + 0); - /* frees op->customdata */ - paint_stroke_exec(C, op); + /* frees op->customdata */ + paint_stroke_exec(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void wpaint_cancel(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - if (ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } + Object *ob = CTX_data_active_object(C); + if (ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op); } void PAINT_OT_weight_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Weight Paint"; - ot->idname = "PAINT_OT_weight_paint"; - ot->description = "Paint a stroke in the current vertex group's weights"; + /* identifiers */ + ot->name = "Weight Paint"; + ot->idname = "PAINT_OT_weight_paint"; + ot->description = "Paint a stroke in the current vertex group's weights"; - /* api callbacks */ - ot->invoke = wpaint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = wpaint_exec; - ot->poll = weight_paint_poll; - ot->cancel = wpaint_cancel; + /* api callbacks */ + ot->invoke = wpaint_invoke; + ot->modal = paint_stroke_modal; + ot->exec = wpaint_exec; + ot->poll = weight_paint_poll; + ot->cancel = wpaint_cancel; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot); } /* ************ set / clear vertex paint mode ********** */ @@ -2392,64 +2446,62 @@ void PAINT_OT_weight_paint(wmOperatorType *ot) */ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Object *ob = CTX_data_active_object(C); - const int mode_flag = OB_MODE_VERTEX_PAINT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; + Main *bmain = CTX_data_main(C); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Object *ob = CTX_data_active_object(C); + const int mode_flag = OB_MODE_VERTEX_PAINT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } - Mesh *me = BKE_mesh_from_object(ob); + Mesh *me = BKE_mesh_from_object(ob); - /* toggle: end vpaint */ - if (is_mode_set) { - ED_object_vpaintmode_exit_ex(ob); - } - else { - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - wmWindowManager *wm = CTX_wm_manager(C); - ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); - BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint); - } + /* toggle: end vpaint */ + if (is_mode_set) { + ED_object_vpaintmode_exit_ex(ob); + } + else { + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + wmWindowManager *wm = CTX_wm_manager(C); + ED_object_vpaintmode_enter_ex(bmain, depsgraph, wm, scene, ob); + BKE_paint_toolslots_brush_validate(bmain, &ts->vpaint->paint); + } - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - /* update modifier stack for mapping requirements */ - DEG_id_tag_update(&me->id, 0); + /* update modifier stack for mapping requirements */ + DEG_id_tag_update(&me->id, 0); - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - WM_toolsystem_update_from_context_view3d(C); + WM_toolsystem_update_from_context_view3d(C); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint Mode"; - ot->idname = "PAINT_OT_vertex_paint_toggle"; - ot->description = "Toggle the vertex paint mode in 3D view"; + /* identifiers */ + ot->name = "Vertex Paint Mode"; + ot->idname = "PAINT_OT_vertex_paint_toggle"; + ot->description = "Toggle the vertex paint mode in 3D view"; - /* api callbacks */ - ot->exec = vpaint_mode_toggle_exec; - ot->poll = paint_poll_test; + /* api callbacks */ + ot->exec = vpaint_mode_toggle_exec; + ot->poll = paint_poll_test; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } - - /* ********************** vertex paint operator ******************* */ /* Implementation notes: @@ -2471,815 +2523,847 @@ void PAINT_OT_vertex_paint_toggle(wmOperatorType *ot) */ typedef struct PolyFaceMap { - struct PolyFaceMap *next, *prev; - int facenr; + struct PolyFaceMap *next, *prev; + int facenr; } PolyFaceMap; struct VPaintData { - ViewContext vc; - struct NormalAnglePrecalc normal_angle_precalc; + ViewContext vc; + struct NormalAnglePrecalc normal_angle_precalc; - uint paintcol; + uint paintcol; - struct VertProjHandle *vp_handle; - struct CoNo *vertexcosnos; + struct VertProjHandle *vp_handle; + struct CoNo *vertexcosnos; - /* modify 'me->mcol' directly, since the derived mesh is drawing from this - * array, otherwise we need to refresh the modifier stack */ - bool use_fast_update; + /* modify 'me->mcol' directly, since the derived mesh is drawing from this + * array, otherwise we need to refresh the modifier stack */ + bool use_fast_update; - /* loops tagged as having been painted, to apply shared vertex color - * blending only to modified loops */ - bool *mlooptag; + /* loops tagged as having been painted, to apply shared vertex color + * blending only to modified loops */ + bool *mlooptag; - bool is_texbrush; + bool is_texbrush; - /* Special storage for smear brush, avoid feedback loop - update each step. */ - struct { - uint *color_prev; - uint *color_curr; - } smear; + /* Special storage for smear brush, avoid feedback loop - update each step. */ + struct { + uint *color_prev; + uint *color_curr; + } smear; }; static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - struct PaintStroke *stroke = op->customdata; - VPaint *vp = ts->vpaint; - Brush *brush = BKE_paint_brush(&vp->paint); - struct VPaintData *vpd; - Object *ob = CTX_data_active_object(C); - Mesh *me; - SculptSession *ss = ob->sculpt; - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - /* context checks could be a poll() */ - me = BKE_mesh_from_object(ob); - if (me == NULL || me->totpoly == 0) - return false; - - ED_mesh_color_ensure(me, NULL); - if (me->mloopcol == NULL) - return false; - - /* make mode data storage */ - vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); - paint_stroke_set_mode_data(stroke, vpd); - ED_view3d_viewcontext_init(C, &vpd->vc); - view_angle_limits_init( - &vpd->normal_angle_precalc, vp->paint.brush->falloff_angle, - (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); - - vpd->paintcol = vpaint_get_current_col(scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); - - vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; - - /* are we painting onto a modified mesh?, - * if not we can skip face map trickiness */ - if (vertex_paint_use_fast_update_check(ob)) { - vpd->use_fast_update = true; -/* printf("Fast update!\n");*/ - } - else { - vpd->use_fast_update = false; -/* printf("No fast update!\n");*/ - } - - /* to keep tracked of modified loops for shared vertex color blending */ - if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) { - vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); - } - - if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { - vpd->smear.color_prev = MEM_mallocN(sizeof(uint) * me->totloop, __func__); - memcpy(vpd->smear.color_prev, me->mloopcol, sizeof(uint) * me->totloop); - vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev); - } - - /* Create projection handle */ - if (vpd->is_texbrush) { - ob->sculpt->building_vp_handle = true; - vpd->vp_handle = ED_vpaint_proj_handle_create(depsgraph, scene, ob, &vpd->vertexcosnos); - ob->sculpt->building_vp_handle = false; - } - - /* If not previously created, create vertex/weight paint mode session data */ - vertex_paint_init_stroke(depsgraph, scene, ob); - vwpaint_update_cache_invariants(C, vp, ss, op, mouse); - vertex_paint_init_session_data(ts, ob); - - if (ob->sculpt->mode.vpaint.previous_color != NULL) { - memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop); - } - - return 1; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + struct PaintStroke *stroke = op->customdata; + VPaint *vp = ts->vpaint; + Brush *brush = BKE_paint_brush(&vp->paint); + struct VPaintData *vpd; + Object *ob = CTX_data_active_object(C); + Mesh *me; + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + /* context checks could be a poll() */ + me = BKE_mesh_from_object(ob); + if (me == NULL || me->totpoly == 0) + return false; + + ED_mesh_color_ensure(me, NULL); + if (me->mloopcol == NULL) + return false; + + /* make mode data storage */ + vpd = MEM_callocN(sizeof(*vpd), "VPaintData"); + paint_stroke_set_mode_data(stroke, vpd); + ED_view3d_viewcontext_init(C, &vpd->vc); + view_angle_limits_init(&vpd->normal_angle_precalc, + vp->paint.brush->falloff_angle, + (vp->paint.brush->flag & BRUSH_FRONTFACE_FALLOFF) != 0); + + vpd->paintcol = vpaint_get_current_col( + scene, vp, (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT)); + + vpd->is_texbrush = !(brush->vertexpaint_tool == VPAINT_TOOL_BLUR) && brush->mtex.tex; + + /* are we painting onto a modified mesh?, + * if not we can skip face map trickiness */ + if (vertex_paint_use_fast_update_check(ob)) { + vpd->use_fast_update = true; + /* printf("Fast update!\n");*/ + } + else { + vpd->use_fast_update = false; + /* printf("No fast update!\n");*/ + } + + /* to keep tracked of modified loops for shared vertex color blending */ + if (brush->vertexpaint_tool == VPAINT_TOOL_BLUR) { + vpd->mlooptag = MEM_mallocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); + } + + if (brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { + vpd->smear.color_prev = MEM_mallocN(sizeof(uint) * me->totloop, __func__); + memcpy(vpd->smear.color_prev, me->mloopcol, sizeof(uint) * me->totloop); + vpd->smear.color_curr = MEM_dupallocN(vpd->smear.color_prev); + } + + /* Create projection handle */ + if (vpd->is_texbrush) { + ob->sculpt->building_vp_handle = true; + vpd->vp_handle = ED_vpaint_proj_handle_create(depsgraph, scene, ob, &vpd->vertexcosnos); + ob->sculpt->building_vp_handle = false; + } + + /* If not previously created, create vertex/weight paint mode session data */ + vertex_paint_init_stroke(depsgraph, scene, ob); + vwpaint_update_cache_invariants(C, vp, ss, op, mouse); + vertex_paint_init_session_data(ts, ob); + + if (ob->sculpt->mode.vpaint.previous_color != NULL) { + memset(ob->sculpt->mode.vpaint.previous_color, 0, sizeof(uint) * me->totloop); + } + + return 1; } static void do_vpaint_brush_calc_average_color_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - - StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - char *col; - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - - struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n; - accum->len = 0; - memset(accum->value, 0, sizeof(accum->value)); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) { - /* If the vertex is selected for painting. */ - const MVert *mv = &data->me->mvert[v_index]; - if (!use_vert_sel || mv->flag & SELECT) { - accum->len += gmap->vert_to_loop[v_index].count; - /* if a vertex is within the brush region, then add it's color to the blend. */ - for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - col = (char *)(&lcol[l_index]); - /* Color is squared to compensate the sqrt color encoding. */ - accum->value[0] += col[0] * col[0]; - accum->value[1] += col[1] * col[1]; - accum->value[2] += col[2] * col[2]; - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static float tex_color_alpha_ubyte( - SculptThreadedTaskData *data, const float v_co[3], - uint *r_color) -{ - float rgba[4]; - float rgba_br[3]; - tex_color_alpha(data->vp, &data->vpd->vc, v_co, rgba); - rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol); - mul_v3_v3(rgba_br, rgba); - rgb_float_to_uchar((uchar *)r_color, rgba_br); - return rgba[3]; -} - -static void do_vpaint_brush_draw_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - const Scene *scene = CTX_data_scene(data->C); - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* Note: Grids are 1:1 with corners (aka loops). - * For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &data->me->mvert[v_index]; - - /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { - /* Calc the dot prod. between ray norm on surf and current vert - * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - uint color_final = data->vpd->paintcol; - - /* If we're painting with a texture, sample the texture color and alpha. */ - float tex_alpha = 1.0; - if (data->vpd->is_texbrush) { - /* Note: we may want to paint alpha as vertex color alpha. */ - tex_alpha = tex_color_alpha_ubyte( - data, data->vpd->vertexcosnos[v_index].co, - &color_final); - } - /* For each poly owning this vert, paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; - } - const float final_alpha = - 255 * brush_fade * brush_strength * - tex_alpha * brush_alpha_pressure * grid_alpha; - - /* Mix the new color with the original based on final_alpha. */ - lcol[l_index] = vpaint_blend( - data->vp, lcol[l_index], color_orig, color_final, - final_alpha, 255 * brush_strength); - } - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_vpaint_brush_blur_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - Scene *scene = CTX_data_scene(data->C); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv = &data->me->mvert[v_index]; - - /* If the vertex is selected for painting. */ - if (!use_vert_sel || mv->flag & SELECT) { - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - - /* Get the average poly color */ - uint color_final = 0; - int total_hit_loops = 0; - uint blend[4] = {0}; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - int p_index = gmap->vert_to_poly[v_index].indices[j]; - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - total_hit_loops += mp->totloop; - for (int k = 0; k < mp->totloop; k++) { - const uint l_index = mp->loopstart + k; - const char *col = (const char *)(&lcol[l_index]); - /* Color is squared to compensate the sqrt color encoding. */ - blend[0] += (uint)col[0] * (uint)col[0]; - blend[1] += (uint)col[1] * (uint)col[1]; - blend[2] += (uint)col[2] * (uint)col[2]; - blend[3] += (uint)col[3] * (uint)col[3]; - } - } - } - if (total_hit_loops != 0) { - /* Use rgb^2 color averaging. */ - char *col = (char *)(&color_final); - col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops))); - col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops))); - col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops))); - col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops))); - - /* For each poly owning this vert, - * paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; - } - const float final_alpha = - 255 * brush_fade * brush_strength * - brush_alpha_pressure * grid_alpha; - /* Mix the new color with the original - * based on the brush strength and the curve. */ - lcol[l_index] = vpaint_blend( - data->vp, lcol[l_index], color_orig, *((uint *)col), - final_alpha, 255 * brush_strength); - } - } - } - } - } - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_vpaint_brush_smear_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); - const bool has_grids = (pbvh_type == PBVH_GRIDS); - - Scene *scene = CTX_data_scene(data->C); - const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; - const Brush *brush = data->brush; - const StrokeCache *cache = ss->cache; - uint *lcol = data->lcol; - float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; - get_brush_alpha_data(scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); - float brush_dir[3]; - const bool use_normal = vwpaint_use_normal(data->vp); - const bool use_vert_sel = (data->me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; - const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - - sub_v3_v3v3(brush_dir, cache->location, cache->last_location); - project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); - - if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - const float *sculpt_normal_frontface = - sculpt_brush_frontface_normal_from_falloff_shape(ss, data->brush->falloff_shape); - - /* For each vertex */ - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - /* Test to see if the vertex coordinates are within the spherical brush region. */ - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* For grid based pbvh, take the vert whose loop corresponds to the current grid. - * Otherwise, take the current vert. */ - const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; - const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; - const MVert *mv_curr = &data->me->mvert[v_index]; - - /* if the vertex is selected for painting. */ - if (!use_vert_sel || mv_curr->flag & SELECT) { - /* Calc the dot prod. between ray norm on surf and current vert - * (ie splash prevention factor), and only paint front facing verts. */ - float brush_strength = cache->bstrength; - const float angle_cos = (use_normal && vd.no) ? - dot_vf3vs3(sculpt_normal_frontface, vd.no) : 1.0f; - if (((brush->flag & BRUSH_FRONTFACE) == 0 || - (angle_cos > 0.0f)) && - ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || - view_angle_limits_apply_falloff(&data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) - { - const float brush_fade = BKE_brush_curve_strength(brush, sqrtf(test.dist), cache->radius); - - bool do_color = false; - /* Minimum dot product between brush direction and current - * to neighbor direction is 0.0, meaning orthogonal. */ - float stroke_dot_max = 0.0f; - - /* Get the color of the loop in the opposite - * direction of the brush movement */ - uint color_final = 0; - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - UNUSED_VARS_NDEBUG(l_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - const MLoop *ml_other = &data->me->mloop[mp->loopstart]; - for (int k = 0; k < mp->totloop; k++, ml_other++) { - const uint v_other_index = ml_other->v; - if (v_other_index != v_index) { - const MVert *mv_other = &data->me->mvert[v_other_index]; - - /* Get the direction from the - * selected vert to the neighbor. */ - float other_dir[3]; - sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); - project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); - - normalize_v3(other_dir); - - const float stroke_dot = dot_v3v3(other_dir, brush_dir); - - if (stroke_dot > stroke_dot_max) { - stroke_dot_max = stroke_dot; - color_final = data->vpd->smear.color_prev[mp->loopstart + k]; - do_color = true; - } - } - } - } - } - - if (do_color) { - const float final_alpha = - 255 * brush_fade * brush_strength * - brush_alpha_pressure * grid_alpha; - - /* For each poly owning this vert, - * paint each loop belonging to this vert. */ - for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { - const int p_index = gmap->vert_to_poly[v_index].indices[j]; - const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(data->me->mloop[l_index].v == v_index); - const MPoly *mp = &data->me->mpoly[p_index]; - if (!use_face_sel || mp->flag & ME_FACE_SEL) { - /* Get the previous loop color */ - uint color_orig = 0; /* unused when array is NULL */ - if (ss->mode.vpaint.previous_color != NULL) { - /* Get the previous loop color */ - if (ss->mode.vpaint.previous_color[l_index] == 0) { - ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; - } - color_orig = ss->mode.vpaint.previous_color[l_index]; - } - /* Mix the new color with the original - * based on the brush strength and the curve. */ - lcol[l_index] = vpaint_blend( - data->vp, lcol[l_index], color_orig, color_final, - final_alpha, 255 * brush_strength); - - data->vpd->smear.color_curr[l_index] = lcol[l_index]; - } - } - } - } - } - } - } - BKE_pbvh_vertex_iter_end; - } -} - -static void calculate_average_color(SculptThreadedTaskData *data, PBVHNode **UNUSED(nodes), int totnode) -{ - struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); - data->custom_data = accum; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range( - 0, totnode, - data, - do_vpaint_brush_calc_average_color_cb_ex, - &settings); - - uint accum_len = 0; - uint accum_value[3] = {0}; - uchar blend[4] = {0}; - for (int i = 0; i < totnode; i++) { - accum_len += accum[i].len; - accum_value[0] += accum[i].value[0]; - accum_value[1] += accum[i].value[1]; - accum_value[2] += accum[i].value[2]; - } - if (accum_len != 0) { - blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len))); - blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len))); - blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len))); - blend[3] = 255; - data->vpd->paintcol = *((uint *)blend); - } - - MEM_SAFE_FREE(data->custom_data); /* 'accum' */ -} - -static void vpaint_paint_leaves( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, - Object *ob, Mesh *me, PBVHNode **nodes, int totnode) -{ - const Brush *brush = ob->sculpt->cache->brush; - - SculptThreadedTaskData data = { - .C = C, .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .vp = vp, .vpd = vpd, - .lcol = (uint *)me->mloopcol, .me = me, - }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { - case VPAINT_TOOL_AVERAGE: - calculate_average_color(&data, nodes, totnode); - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_draw_task_cb_ex, - &settings); - break; - case VPAINT_TOOL_BLUR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_blur_task_cb_ex, - &settings); - break; - case VPAINT_TOOL_SMEAR: - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_smear_task_cb_ex, - &settings); - break; - case VPAINT_TOOL_DRAW: - BLI_task_parallel_range( - 0, totnode, - &data, - do_vpaint_brush_draw_task_cb_ex, - &settings); - break; - } -} - -static void vpaint_do_paint( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, - Object *ob, Mesh *me, Brush *brush, const char symm, const int axis, const int i, const float angle) -{ - SculptSession *ss = ob->sculpt; - ss->cache->radial_symmetry_pass = i; - sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - - int totnode; - PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode); - - /* Paint those leaves. */ - vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode); - - if (nodes) { - MEM_freeN(nodes); - } -} - -static void vpaint_do_radial_symmetry( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob, Mesh *me, - Brush *brush, const char symm, const int axis) -{ - for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { - const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle); - } + void *__restrict userdata, const int n, const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + + StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + char *col; + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + + struct VPaintAverageAccum *accum = (struct VPaintAverageAccum *)data->custom_data + n; + accum->len = 0; + memset(accum->value, 0, sizeof(accum->value)); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + if (BKE_brush_curve_strength(data->brush, 0.0, cache->radius) > 0.0) { + /* If the vertex is selected for painting. */ + const MVert *mv = &data->me->mvert[v_index]; + if (!use_vert_sel || mv->flag & SELECT) { + accum->len += gmap->vert_to_loop[v_index].count; + /* if a vertex is within the brush region, then add it's color to the blend. */ + for (int j = 0; j < gmap->vert_to_loop[v_index].count; j++) { + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + col = (char *)(&lcol[l_index]); + /* Color is squared to compensate the sqrt color encoding. */ + accum->value[0] += col[0] * col[0]; + accum->value[1] += col[1] * col[1]; + accum->value[2] += col[2] * col[2]; + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static float tex_color_alpha_ubyte(SculptThreadedTaskData *data, + const float v_co[3], + uint *r_color) +{ + float rgba[4]; + float rgba_br[3]; + tex_color_alpha(data->vp, &data->vpd->vc, v_co, rgba); + rgb_uchar_to_float(rgba_br, (const uchar *)&data->vpd->paintcol); + mul_v3_v3(rgba_br, rgba); + rgb_float_to_uchar((uchar *)r_color, rgba_br); + return rgba[3]; +} + +static void do_vpaint_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + const Scene *scene = CTX_data_scene(data->C); + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* Note: Grids are 1:1 with corners (aka loops). + * For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &data->me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + /* Calc the dot prod. between ray norm on surf and current vert + * (ie splash prevention factor), and only paint front facing verts. */ + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + uint color_final = data->vpd->paintcol; + + /* If we're painting with a texture, sample the texture color and alpha. */ + float tex_alpha = 1.0; + if (data->vpd->is_texbrush) { + /* Note: we may want to paint alpha as vertex color alpha. */ + tex_alpha = tex_color_alpha_ubyte( + data, data->vpd->vertexcosnos[v_index].co, &color_final); + } + /* For each poly owning this vert, paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + uint color_orig = 0; /* unused when array is NULL */ + if (ss->mode.vpaint.previous_color != NULL) { + /* Get the previous loop color */ + if (ss->mode.vpaint.previous_color[l_index] == 0) { + ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + } + color_orig = ss->mode.vpaint.previous_color[l_index]; + } + const float final_alpha = 255 * brush_fade * brush_strength * tex_alpha * + brush_alpha_pressure * grid_alpha; + + /* Mix the new color with the original based on final_alpha. */ + lcol[l_index] = vpaint_blend(data->vp, + lcol[l_index], + color_orig, + color_final, + final_alpha, + 255 * brush_strength); + } + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_vpaint_brush_blur_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + Scene *scene = CTX_data_scene(data->C); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv = &data->me->mvert[v_index]; + + /* If the vertex is selected for painting. */ + if (!use_vert_sel || mv->flag & SELECT) { + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + + /* Get the average poly color */ + uint color_final = 0; + int total_hit_loops = 0; + uint blend[4] = {0}; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + int p_index = gmap->vert_to_poly[v_index].indices[j]; + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + total_hit_loops += mp->totloop; + for (int k = 0; k < mp->totloop; k++) { + const uint l_index = mp->loopstart + k; + const char *col = (const char *)(&lcol[l_index]); + /* Color is squared to compensate the sqrt color encoding. */ + blend[0] += (uint)col[0] * (uint)col[0]; + blend[1] += (uint)col[1] * (uint)col[1]; + blend[2] += (uint)col[2] * (uint)col[2]; + blend[3] += (uint)col[3] * (uint)col[3]; + } + } + } + if (total_hit_loops != 0) { + /* Use rgb^2 color averaging. */ + char *col = (char *)(&color_final); + col[0] = round_fl_to_uchar(sqrtf(divide_round_i(blend[0], total_hit_loops))); + col[1] = round_fl_to_uchar(sqrtf(divide_round_i(blend[1], total_hit_loops))); + col[2] = round_fl_to_uchar(sqrtf(divide_round_i(blend[2], total_hit_loops))); + col[3] = round_fl_to_uchar(sqrtf(divide_round_i(blend[3], total_hit_loops))); + + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + uint color_orig = 0; /* unused when array is NULL */ + if (ss->mode.vpaint.previous_color != NULL) { + /* Get the previous loop color */ + if (ss->mode.vpaint.previous_color[l_index] == 0) { + ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + } + color_orig = ss->mode.vpaint.previous_color[l_index]; + } + const float final_alpha = 255 * brush_fade * brush_strength * + brush_alpha_pressure * grid_alpha; + /* Mix the new color with the original + * based on the brush strength and the curve. */ + lcol[l_index] = vpaint_blend(data->vp, + lcol[l_index], + color_orig, + *((uint *)col), + final_alpha, + 255 * brush_strength); + } + } + } + } + } + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_vpaint_brush_smear_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const PBVHType pbvh_type = BKE_pbvh_type(ss->pbvh); + const bool has_grids = (pbvh_type == PBVH_GRIDS); + + Scene *scene = CTX_data_scene(data->C); + const struct SculptVertexPaintGeomMap *gmap = &ss->mode.vpaint.gmap; + const Brush *brush = data->brush; + const StrokeCache *cache = ss->cache; + uint *lcol = data->lcol; + float brush_size_pressure, brush_alpha_value, brush_alpha_pressure; + get_brush_alpha_data( + scene, ss, brush, &brush_size_pressure, &brush_alpha_value, &brush_alpha_pressure); + float brush_dir[3]; + const bool use_normal = vwpaint_use_normal(data->vp); + const bool use_vert_sel = (data->me->editflag & + (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; + const bool use_face_sel = (data->me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + sub_v3_v3v3(brush_dir, cache->location, cache->last_location); + project_plane_v3_v3v3(brush_dir, brush_dir, cache->view_normal); + + if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + const float *sculpt_normal_frontface = sculpt_brush_frontface_normal_from_falloff_shape( + ss, data->brush->falloff_shape); + + /* For each vertex */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + /* Test to see if the vertex coordinates are within the spherical brush region. */ + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* For grid based pbvh, take the vert whose loop corresponds to the current grid. + * Otherwise, take the current vert. */ + const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : + vd.vert_indices[vd.i]; + const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; + const MVert *mv_curr = &data->me->mvert[v_index]; + + /* if the vertex is selected for painting. */ + if (!use_vert_sel || mv_curr->flag & SELECT) { + /* Calc the dot prod. between ray norm on surf and current vert + * (ie splash prevention factor), and only paint front facing verts. */ + float brush_strength = cache->bstrength; + const float angle_cos = (use_normal && vd.no) ? + dot_vf3vs3(sculpt_normal_frontface, vd.no) : + 1.0f; + if (((brush->flag & BRUSH_FRONTFACE) == 0 || (angle_cos > 0.0f)) && + ((brush->flag & BRUSH_FRONTFACE_FALLOFF) == 0 || + view_angle_limits_apply_falloff( + &data->vpd->normal_angle_precalc, angle_cos, &brush_strength))) { + const float brush_fade = BKE_brush_curve_strength( + brush, sqrtf(test.dist), cache->radius); + + bool do_color = false; + /* Minimum dot product between brush direction and current + * to neighbor direction is 0.0, meaning orthogonal. */ + float stroke_dot_max = 0.0f; + + /* Get the color of the loop in the opposite + * direction of the brush movement */ + uint color_final = 0; + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + UNUSED_VARS_NDEBUG(l_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + const MLoop *ml_other = &data->me->mloop[mp->loopstart]; + for (int k = 0; k < mp->totloop; k++, ml_other++) { + const uint v_other_index = ml_other->v; + if (v_other_index != v_index) { + const MVert *mv_other = &data->me->mvert[v_other_index]; + + /* Get the direction from the + * selected vert to the neighbor. */ + float other_dir[3]; + sub_v3_v3v3(other_dir, mv_curr->co, mv_other->co); + project_plane_v3_v3v3(other_dir, other_dir, cache->view_normal); + + normalize_v3(other_dir); + + const float stroke_dot = dot_v3v3(other_dir, brush_dir); + + if (stroke_dot > stroke_dot_max) { + stroke_dot_max = stroke_dot; + color_final = data->vpd->smear.color_prev[mp->loopstart + k]; + do_color = true; + } + } + } + } + } + + if (do_color) { + const float final_alpha = 255 * brush_fade * brush_strength * brush_alpha_pressure * + grid_alpha; + + /* For each poly owning this vert, + * paint each loop belonging to this vert. */ + for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { + const int p_index = gmap->vert_to_poly[v_index].indices[j]; + const int l_index = gmap->vert_to_loop[v_index].indices[j]; + BLI_assert(data->me->mloop[l_index].v == v_index); + const MPoly *mp = &data->me->mpoly[p_index]; + if (!use_face_sel || mp->flag & ME_FACE_SEL) { + /* Get the previous loop color */ + uint color_orig = 0; /* unused when array is NULL */ + if (ss->mode.vpaint.previous_color != NULL) { + /* Get the previous loop color */ + if (ss->mode.vpaint.previous_color[l_index] == 0) { + ss->mode.vpaint.previous_color[l_index] = lcol[l_index]; + } + color_orig = ss->mode.vpaint.previous_color[l_index]; + } + /* Mix the new color with the original + * based on the brush strength and the curve. */ + lcol[l_index] = vpaint_blend(data->vp, + lcol[l_index], + color_orig, + color_final, + final_alpha, + 255 * brush_strength); + + data->vpd->smear.color_curr[l_index] = lcol[l_index]; + } + } + } + } + } + } + } + BKE_pbvh_vertex_iter_end; + } +} + +static void calculate_average_color(SculptThreadedTaskData *data, + PBVHNode **UNUSED(nodes), + int totnode) +{ + struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); + data->custom_data = accum; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); + + uint accum_len = 0; + uint accum_value[3] = {0}; + uchar blend[4] = {0}; + for (int i = 0; i < totnode; i++) { + accum_len += accum[i].len; + accum_value[0] += accum[i].value[0]; + accum_value[1] += accum[i].value[1]; + accum_value[2] += accum[i].value[2]; + } + if (accum_len != 0) { + blend[0] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[0], accum_len))); + blend[1] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[1], accum_len))); + blend[2] = round_fl_to_uchar(sqrtf(divide_round_i(accum_value[2], accum_len))); + blend[3] = 255; + data->vpd->paintcol = *((uint *)blend); + } + + MEM_SAFE_FREE(data->custom_data); /* 'accum' */ +} + +static void vpaint_paint_leaves(bContext *C, + Sculpt *sd, + VPaint *vp, + struct VPaintData *vpd, + Object *ob, + Mesh *me, + PBVHNode **nodes, + int totnode) +{ + const Brush *brush = ob->sculpt->cache->brush; + + SculptThreadedTaskData data = { + .C = C, + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .vp = vp, + .vpd = vpd, + .lcol = (uint *)me->mloopcol, + .me = me, + }; + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { + case VPAINT_TOOL_AVERAGE: + calculate_average_color(&data, nodes, totnode); + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + break; + case VPAINT_TOOL_BLUR: + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); + break; + case VPAINT_TOOL_SMEAR: + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); + break; + case VPAINT_TOOL_DRAW: + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + break; + } +} + +static void vpaint_do_paint(bContext *C, + Sculpt *sd, + VPaint *vp, + struct VPaintData *vpd, + Object *ob, + Mesh *me, + Brush *brush, + const char symm, + const int axis, + const int i, + const float angle) +{ + SculptSession *ss = ob->sculpt; + ss->cache->radial_symmetry_pass = i; + sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); + + int totnode; + PBVHNode **nodes = vwpaint_pbvh_gather_generic(ob, vp, sd, brush, &totnode); + + /* Paint those leaves. */ + vpaint_paint_leaves(C, sd, vp, vpd, ob, me, nodes, totnode); + + if (nodes) { + MEM_freeN(nodes); + } +} + +static void vpaint_do_radial_symmetry(bContext *C, + Sculpt *sd, + VPaint *vp, + struct VPaintData *vpd, + Object *ob, + Mesh *me, + Brush *brush, + const char symm, + const int axis) +{ + for (int i = 1; i < vp->radial_symm[axis - 'X']; i++) { + const float angle = (2.0 * M_PI) * i / vp->radial_symm[axis - 'X']; + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, symm, axis, i, angle); + } } /* near duplicate of: sculpt.c's, * 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */ static void vpaint_do_symmetrical_brush_actions( - bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob) -{ - Brush *brush = BKE_paint_brush(&vp->paint); - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const char symm = vp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - int i = 0; - - /* initial stroke */ - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); - - cache->symmetry = symm; - - /* symm is a bit combination of XYZ - 1 is mirror - * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 1; i <= symm; i++) { - if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { - cache->mirror_symmetry_pass = i; - cache->radial_symmetry_pass = 0; - sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); - - if (i & (1 << 0)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); - } - if (i & (1 << 1)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); - } - if (i & (1 << 2)) { - vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); - vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); - } - } - } - - copy_v3_v3(cache->true_last_location, cache->true_location); - cache->is_last_valid = true; + bContext *C, Sculpt *sd, VPaint *vp, struct VPaintData *vpd, Object *ob) +{ + Brush *brush = BKE_paint_brush(&vp->paint); + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const char symm = vp->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + int i = 0; + + /* initial stroke */ + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); + + cache->symmetry = symm; + + /* symm is a bit combination of XYZ - 1 is mirror + * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for (i = 1; i <= symm; i++) { + if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { + cache->mirror_symmetry_pass = i; + cache->radial_symmetry_pass = 0; + sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); + + if (i & (1 << 0)) { + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'X', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'X'); + } + if (i & (1 << 1)) { + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Y', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Y'); + } + if (i & (1 << 2)) { + vpaint_do_paint(C, sd, vp, vpd, ob, me, brush, i, 'Z', 0, 0); + vpaint_do_radial_symmetry(C, sd, vp, vpd, ob, me, brush, i, 'Z'); + } + } + } + + copy_v3_v3(cache->true_last_location, cache->true_location); + cache->is_last_valid = true; } static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = CTX_data_tool_settings(C); - struct VPaintData *vpd = paint_stroke_mode_data(stroke); - VPaint *vp = ts->vpaint; - ViewContext *vc = &vpd->vc; - Object *ob = vc->obact; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + struct VPaintData *vpd = paint_stroke_mode_data(stroke); + VPaint *vp = ts->vpaint; + ViewContext *vc = &vpd->vc; + Object *ob = vc->obact; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - vwpaint_update_cache_variants(C, vp, ob, itemptr); + vwpaint_update_cache_variants(C, vp, ob, itemptr); - float mat[4][4]; - float mval[2]; + float mat[4][4]; + float mval[2]; - ED_view3d_init_mats_rv3d(ob, vc->rv3d); + ED_view3d_init_mats_rv3d(ob, vc->rv3d); - /* load projection matrix */ - mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); + /* load projection matrix */ + mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat); - swap_m4m4(vc->rv3d->persmat, mat); + swap_m4m4(vc->rv3d->persmat, mat); - vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob); + vpaint_do_symmetrical_brush_actions(C, sd, vp, vpd, ob); - swap_m4m4(vc->rv3d->persmat, mat); + swap_m4m4(vc->rv3d->persmat, mat); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { - memcpy(vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop); - } + if (vp->paint.brush->vertexpaint_tool == VPAINT_TOOL_SMEAR) { + memcpy( + vpd->smear.color_prev, vpd->smear.color_curr, sizeof(uint) * ((Mesh *)ob->data)->totloop); + } - /* calculate pivot for rotation around seletion if needed */ - /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, vc->ar, mval); + /* calculate pivot for rotation around seletion if needed */ + /* also needed for "View Selected" on last stroke */ + paint_last_stroke_update(scene, vc->ar, mval); - ED_region_tag_redraw(vc->ar); + ED_region_tag_redraw(vc->ar); - if (vpd->use_fast_update == false) { - /* recalculate modifier stack to get new colors, slow, - * avoid this if we can! */ - DEG_id_tag_update(ob->data, 0); - } - else { - /* Flush changes through DEG. */ - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE); - } + if (vpd->use_fast_update == false) { + /* recalculate modifier stack to get new colors, slow, + * avoid this if we can! */ + DEG_id_tag_update(ob->data, 0); + } + else { + /* Flush changes through DEG. */ + DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE); + } } static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) { - struct VPaintData *vpd = paint_stroke_mode_data(stroke); - ViewContext *vc = &vpd->vc; - Object *ob = vc->obact; + struct VPaintData *vpd = paint_stroke_mode_data(stroke); + ViewContext *vc = &vpd->vc; + Object *ob = vc->obact; - if (vpd->is_texbrush) { - ED_vpaint_proj_handle_free(vpd->vp_handle); - } + if (vpd->is_texbrush) { + ED_vpaint_proj_handle_free(vpd->vp_handle); + } - if (vpd->mlooptag) - MEM_freeN(vpd->mlooptag); - if (vpd->smear.color_prev) - MEM_freeN(vpd->smear.color_prev); - if (vpd->smear.color_curr) - MEM_freeN(vpd->smear.color_curr); + if (vpd->mlooptag) + MEM_freeN(vpd->mlooptag); + if (vpd->smear.color_prev) + MEM_freeN(vpd->smear.color_prev); + if (vpd->smear.color_curr) + MEM_freeN(vpd->smear.color_curr); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - MEM_freeN(vpd); + MEM_freeN(vpd); - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; } static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int retval; + int retval; - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, vpaint_stroke_test_start, - vpaint_stroke_update_step, NULL, - vpaint_stroke_done, event->type); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + vpaint_stroke_test_start, + vpaint_stroke_update_step, + NULL, + vpaint_stroke_done, + event->type); - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } - /* add modal handler */ - WM_event_add_modal_handler(C, op); + /* add modal handler */ + WM_event_add_modal_handler(C, op); - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int vpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new( - C, op, sculpt_stroke_get_location, vpaint_stroke_test_start, - vpaint_stroke_update_step, NULL, - vpaint_stroke_done, 0); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + vpaint_stroke_test_start, + vpaint_stroke_update_step, + NULL, + vpaint_stroke_done, + 0); - /* frees op->customdata */ - paint_stroke_exec(C, op); + /* frees op->customdata */ + paint_stroke_exec(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void vpaint_cancel(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - if (ob->sculpt->cache) { - sculpt_cache_free(ob->sculpt->cache); - ob->sculpt->cache = NULL; - } + Object *ob = CTX_data_active_object(C); + if (ob->sculpt->cache) { + sculpt_cache_free(ob->sculpt->cache); + ob->sculpt->cache = NULL; + } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op); } void PAINT_OT_vertex_paint(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint"; - ot->idname = "PAINT_OT_vertex_paint"; - ot->description = "Paint a stroke in the active vertex color layer"; + /* identifiers */ + ot->name = "Vertex Paint"; + ot->idname = "PAINT_OT_vertex_paint"; + ot->description = "Paint a stroke in the active vertex color layer"; - /* api callbacks */ - ot->invoke = vpaint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = vpaint_exec; - ot->poll = vertex_paint_poll; - ot->cancel = vpaint_cancel; + /* api callbacks */ + ot->invoke = vpaint_invoke; + ot->modal = paint_stroke_modal; + ot->exec = vpaint_exec; + ot->poll = vertex_paint_poll; + ot->cancel = vpaint_cancel; - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index ae1736581fe..ad7cfebb8d5 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -42,24 +42,23 @@ #include "ED_mesh.h" -#include "paint_intern.h" /* own include */ - +#include "paint_intern.h" /* own include */ static bool vertex_weight_paint_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - Mesh *me = BKE_mesh_from_object(ob); - return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) && - (me && me->totpoly && me->dvert); + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_mesh_from_object(ob); + return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) && + (me && me->totpoly && me->dvert); } static void tag_object_after_update(Object *object) { - BLI_assert(object->type == OB_MESH); - Mesh *mesh = object->data; - DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); - /* NOTE: Original mesh is used for display, so tag it directly here. */ - BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); + BLI_assert(object->type == OB_MESH); + Mesh *mesh = object->data; + DEG_id_tag_update(&mesh->id, ID_RECALC_COPY_ON_WRITE); + /* NOTE: Original mesh is used for display, so tag it directly here. */ + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); } /* -------------------------------------------------------------------- */ @@ -68,74 +67,71 @@ static void tag_object_after_update(Object *object) static bool vertex_color_set(Object *ob, uint paintcol) { - Mesh *me; - const MPoly *mp; - int i, j; - - if (((me = BKE_mesh_from_object(ob)) == NULL) || - (ED_mesh_color_ensure(me, NULL) == false)) - { - return false; - } + Mesh *me; + const MPoly *mp; + int i, j; - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { + return false; + } - mp = me->mpoly; - for (i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = me->mloopcol + mp->loopstart; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) - continue; + mp = me->mpoly; + for (i = 0; i < me->totpoly; i++, mp++) { + MLoopCol *lcol = me->mloopcol + mp->loopstart; - j = 0; - do { - uint vidx = me->mloop[mp->loopstart + j].v; - if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { - *(int *)lcol = paintcol; - } - lcol++; - j++; - } while (j < mp->totloop); + if (use_face_sel && !(mp->flag & ME_FACE_SEL)) + continue; - } + j = 0; + do { + uint vidx = me->mloop[mp->loopstart + j].v; + if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) { + *(int *)lcol = paintcol; + } + lcol++; + j++; + } while (j < mp->totloop); + } - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); + /* remove stale me->mcol, will be added later */ + BKE_mesh_tessface_clear(me); - tag_object_after_update(ob); + tag_object_after_update(ob); - return true; + return true; } static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op)) { - Scene *scene = CTX_data_scene(C); - Object *obact = CTX_data_active_object(C); - unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint, false); - - if (vertex_color_set(obact, paintcol)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Scene *scene = CTX_data_scene(C); + Object *obact = CTX_data_active_object(C); + unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint, false); + + if (vertex_color_set(obact, paintcol)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_set(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Vertex Colors"; - ot->idname = "PAINT_OT_vertex_color_set"; - ot->description = "Fill the active vertex color layer with the current paint color"; + /* identifiers */ + ot->name = "Set Vertex Colors"; + ot->idname = "PAINT_OT_vertex_color_set"; + ot->description = "Fill the active vertex color layer with the current paint color"; - /* api callbacks */ - ot->exec = vertex_color_set_exec; - ot->poll = vertex_paint_mode_poll; + /* api callbacks */ + ot->exec = vertex_color_set_exec; + ot->poll = vertex_paint_mode_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -146,67 +142,65 @@ void PAINT_OT_vertex_color_set(wmOperatorType *ot) static bool vertex_paint_from_weight(Object *ob) { - Mesh *me; - const MPoly *mp; - int vgroup_active; - - if (((me = BKE_mesh_from_object(ob)) == NULL || - (ED_mesh_color_ensure(me, NULL)) == false)) - { - return false; - } - - /* TODO: respect selection. */ - /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway... */ - mp = me->mpoly; - vgroup_active = ob->actdef - 1; - for (int i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = &me->mloopcol[mp->loopstart]; - uint j = 0; - do { - uint vidx = me->mloop[mp->loopstart + j].v; - const float weight = defvert_find_weight(&me->dvert[vidx], vgroup_active); - const uchar grayscale = weight * 255; - lcol->r = grayscale; - lcol->b = grayscale; - lcol->g = grayscale; - lcol++; - j++; - } while (j < mp->totloop); - } - - tag_object_after_update(ob); - - return true; + Mesh *me; + const MPoly *mp; + int vgroup_active; + + if (((me = BKE_mesh_from_object(ob)) == NULL || (ED_mesh_color_ensure(me, NULL)) == false)) { + return false; + } + + /* TODO: respect selection. */ + /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway... */ + mp = me->mpoly; + vgroup_active = ob->actdef - 1; + for (int i = 0; i < me->totpoly; i++, mp++) { + MLoopCol *lcol = &me->mloopcol[mp->loopstart]; + uint j = 0; + do { + uint vidx = me->mloop[mp->loopstart + j].v; + const float weight = defvert_find_weight(&me->dvert[vidx], vgroup_active); + const uchar grayscale = weight * 255; + lcol->r = grayscale; + lcol->b = grayscale; + lcol->g = grayscale; + lcol++; + j++; + } while (j < mp->totloop); + } + + tag_object_after_update(ob); + + return true; } static int vertex_paint_from_weight_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *obact = CTX_data_active_object(C); - if (vertex_paint_from_weight(obact)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *obact = CTX_data_active_object(C); + if (vertex_paint_from_weight(obact)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Color from Weight"; - ot->idname = "PAINT_OT_vertex_color_from_weight"; - ot->description = "Convert active weight into gray scale vertex colors"; + /* identifiers */ + ot->name = "Vertex Color from Weight"; + ot->idname = "PAINT_OT_vertex_color_from_weight"; + ot->description = "Convert active weight into gray scale vertex colors"; - /* api callback */ - ot->exec = vertex_paint_from_weight_exec; - ot->poll = vertex_weight_paint_mode_poll; + /* api callback */ + ot->exec = vertex_paint_from_weight_exec; + ot->poll = vertex_weight_paint_mode_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* TODO: invert, alpha */ + /* TODO: invert, alpha */ } /** \} */ @@ -217,131 +211,129 @@ void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag) { - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - const MPoly *mp; - int (*scol)[4]; - int i, j; - bool has_shared = false; - - /* if no mloopcol: do not do */ - /* if mtexpoly: only the involved faces, otherwise all */ - - if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) return; - - scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol"); - - for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { - if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { - const MLoop *ml = me->mloop + mp->loopstart; - MLoopCol *lcol = me->mloopcol + mp->loopstart; - for (j = 0; j < mp->totloop; j++, ml++, lcol++) { - scol[ml->v][0] += lcol->r; - scol[ml->v][1] += lcol->g; - scol[ml->v][2] += lcol->b; - scol[ml->v][3] += 1; - has_shared = 1; - } - } - } - - if (has_shared) { - for (i = 0; i < me->totvert; i++) { - if (scol[i][3] != 0) { - scol[i][0] = divide_round_i(scol[i][0], scol[i][3]); - scol[i][1] = divide_round_i(scol[i][1], scol[i][3]); - scol[i][2] = divide_round_i(scol[i][2], scol[i][3]); - } - } - - for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { - if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { - const MLoop *ml = me->mloop + mp->loopstart; - MLoopCol *lcol = me->mloopcol + mp->loopstart; - for (j = 0; j < mp->totloop; j++, ml++, lcol++) { - if (mlooptag[mp->loopstart + j]) { - lcol->r = scol[ml->v][0]; - lcol->g = scol[ml->v][1]; - lcol->b = scol[ml->v][2]; - } - } - } - } - } - - MEM_freeN(scol); + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const MPoly *mp; + int(*scol)[4]; + int i, j; + bool has_shared = false; + + /* if no mloopcol: do not do */ + /* if mtexpoly: only the involved faces, otherwise all */ + + if (me->mloopcol == NULL || me->totvert == 0 || me->totpoly == 0) + return; + + scol = MEM_callocN(sizeof(int) * me->totvert * 5, "scol"); + + for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { + if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { + const MLoop *ml = me->mloop + mp->loopstart; + MLoopCol *lcol = me->mloopcol + mp->loopstart; + for (j = 0; j < mp->totloop; j++, ml++, lcol++) { + scol[ml->v][0] += lcol->r; + scol[ml->v][1] += lcol->g; + scol[ml->v][2] += lcol->b; + scol[ml->v][3] += 1; + has_shared = 1; + } + } + } + + if (has_shared) { + for (i = 0; i < me->totvert; i++) { + if (scol[i][3] != 0) { + scol[i][0] = divide_round_i(scol[i][0], scol[i][3]); + scol[i][1] = divide_round_i(scol[i][1], scol[i][3]); + scol[i][2] = divide_round_i(scol[i][2], scol[i][3]); + } + } + + for (i = 0, mp = me->mpoly; i < me->totpoly; i++, mp++) { + if ((use_face_sel == false) || (mp->flag & ME_FACE_SEL)) { + const MLoop *ml = me->mloop + mp->loopstart; + MLoopCol *lcol = me->mloopcol + mp->loopstart; + for (j = 0; j < mp->totloop; j++, ml++, lcol++) { + if (mlooptag[mp->loopstart + j]) { + lcol->r = scol[ml->v][0]; + lcol->g = scol[ml->v][1]; + lcol->b = scol[ml->v][2]; + } + } + } + } + } + + MEM_freeN(scol); } static bool vertex_color_smooth(Object *ob) { - Mesh *me; - const MPoly *mp; + Mesh *me; + const MPoly *mp; - int i, j; + int i, j; - bool *mlooptag; + bool *mlooptag; - if (((me = BKE_mesh_from_object(ob)) == NULL) || - (ED_mesh_color_ensure(me, NULL) == false)) - { - return false; - } + if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { + return false; + } - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); + mlooptag = MEM_callocN(sizeof(bool) * me->totloop, "VPaintData mlooptag"); - /* simply tag loops of selected faces */ - mp = me->mpoly; - for (i = 0; i < me->totpoly; i++, mp++) { - const MLoop *ml = me->mloop + mp->loopstart; - int ml_index = mp->loopstart; + /* simply tag loops of selected faces */ + mp = me->mpoly; + for (i = 0; i < me->totpoly; i++, mp++) { + const MLoop *ml = me->mloop + mp->loopstart; + int ml_index = mp->loopstart; - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) - continue; + if (use_face_sel && !(mp->flag & ME_FACE_SEL)) + continue; - for (j = 0; j < mp->totloop; j++, ml_index++, ml++) { - mlooptag[ml_index] = true; - } - } + for (j = 0; j < mp->totloop; j++, ml_index++, ml++) { + mlooptag[ml_index] = true; + } + } - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); + /* remove stale me->mcol, will be added later */ + BKE_mesh_tessface_clear(me); - vertex_color_smooth_looptag(me, mlooptag); + vertex_color_smooth_looptag(me, mlooptag); - MEM_freeN(mlooptag); + MEM_freeN(mlooptag); - tag_object_after_update(ob); + tag_object_after_update(ob); - return true; + return true; } - static int vertex_color_smooth_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *obact = CTX_data_active_object(C); - if (vertex_color_smooth(obact)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *obact = CTX_data_active_object(C); + if (vertex_color_smooth(obact)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_smooth(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Smooth Vertex Colors"; - ot->idname = "PAINT_OT_vertex_color_smooth"; - ot->description = "Smooth colors across vertices"; + /* identifiers */ + ot->name = "Smooth Vertex Colors"; + ot->idname = "PAINT_OT_vertex_color_smooth"; + ot->description = "Smooth colors across vertices"; - /* api callbacks */ - ot->exec = vertex_color_smooth_exec; - ot->poll = vertex_paint_mode_poll; + /* api callbacks */ + ot->exec = vertex_color_smooth_exec; + ot->poll = vertex_paint_mode_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -351,231 +343,234 @@ void PAINT_OT_vertex_color_smooth(wmOperatorType *ot) * \{ */ struct VPaintTx_BrightContrastData { - /* pre-calculated */ - float gain; - float offset; + /* pre-calculated */ + float gain; + float offset; }; -static void vpaint_tx_brightness_contrast(const float col[3], const void *user_data, float r_col[3]) +static void vpaint_tx_brightness_contrast(const float col[3], + const void *user_data, + float r_col[3]) { - const struct VPaintTx_BrightContrastData *data = user_data; + const struct VPaintTx_BrightContrastData *data = user_data; - for (int i = 0; i < 3; i++) { - r_col[i] = data->gain * col[i] + data->offset; - } + for (int i = 0; i < 3; i++) { + r_col[i] = data->gain * col[i] + data->offset; + } } static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op) { - Object *obact = CTX_data_active_object(C); - - float gain, offset; - { - float brightness = RNA_float_get(op->ptr, "brightness"); - float contrast = RNA_float_get(op->ptr, "contrast"); - brightness /= 100.0f; - float delta = contrast / 200.0f; - gain = 1.0f - delta * 2.0f; - /* - * The algorithm is by Werner D. Streidt - * (http://visca.com/ffactory/archives/5-99/msg00021.html) - * Extracted of OpenCV demhist.c - */ - if (contrast > 0) { - gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON); - offset = gain * (brightness - delta); - } - else { - delta *= -1; - offset = gain * (brightness + delta); - } - } - - const struct VPaintTx_BrightContrastData user_data = { - .gain = gain, - .offset = offset, - }; - - if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *obact = CTX_data_active_object(C); + + float gain, offset; + { + float brightness = RNA_float_get(op->ptr, "brightness"); + float contrast = RNA_float_get(op->ptr, "contrast"); + brightness /= 100.0f; + float delta = contrast / 200.0f; + gain = 1.0f - delta * 2.0f; + /* + * The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + if (contrast > 0) { + gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON); + offset = gain * (brightness - delta); + } + else { + delta *= -1; + offset = gain * (brightness + delta); + } + } + + const struct VPaintTx_BrightContrastData user_data = { + .gain = gain, + .offset = offset, + }; + + if (ED_vpaint_color_transform(obact, vpaint_tx_brightness_contrast, &user_data)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_brightness_contrast(wmOperatorType *ot) { - PropertyRNA *prop; + PropertyRNA *prop; - /* identifiers */ - ot->name = "Vertex Paint Bright/Contrast"; - ot->idname = "PAINT_OT_vertex_color_brightness_contrast"; - ot->description = "Adjust vertex color brightness/contrast"; + /* identifiers */ + ot->name = "Vertex Paint Bright/Contrast"; + ot->idname = "PAINT_OT_vertex_color_brightness_contrast"; + ot->description = "Adjust vertex color brightness/contrast"; - /* api callbacks */ - ot->exec = vertex_color_brightness_contrast_exec; - ot->poll = vertex_paint_mode_poll; + /* api callbacks */ + ot->exec = vertex_color_brightness_contrast_exec; + ot->poll = vertex_paint_mode_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* params */ - const float min = -100, max = +100; - prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max); - prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max); - RNA_def_property_ui_range(prop, min, max, 1, 1); + /* params */ + const float min = -100, max = +100; + prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max); + prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max); + RNA_def_property_ui_range(prop, min, max, 1, 1); } struct VPaintTx_HueSatData { - float hue; - float sat; - float val; + float hue; + float sat; + float val; }; static void vpaint_tx_hsv(const float col[3], const void *user_data, float r_col[3]) { - const struct VPaintTx_HueSatData *data = user_data; - float hsv[3]; - rgb_to_hsv_v(col, hsv); - - hsv[0] += (data->hue - 0.5f); - if (hsv[0] > 1.0f) { - hsv[0] -= 1.0f; - } - else if (hsv[0] < 0.0f) { - hsv[0] += 1.0f; - } - hsv[1] *= data->sat; - hsv[2] *= data->val; - - hsv_to_rgb_v(hsv, r_col); + const struct VPaintTx_HueSatData *data = user_data; + float hsv[3]; + rgb_to_hsv_v(col, hsv); + + hsv[0] += (data->hue - 0.5f); + if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + else if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + hsv[1] *= data->sat; + hsv[2] *= data->val; + + hsv_to_rgb_v(hsv, r_col); } static int vertex_color_hsv_exec(bContext *C, wmOperator *op) { - Object *obact = CTX_data_active_object(C); - - const struct VPaintTx_HueSatData user_data = { - .hue = RNA_float_get(op->ptr, "h"), - .sat = RNA_float_get(op->ptr, "s"), - .val = RNA_float_get(op->ptr, "v"), - }; - - if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *obact = CTX_data_active_object(C); + + const struct VPaintTx_HueSatData user_data = { + .hue = RNA_float_get(op->ptr, "h"), + .sat = RNA_float_get(op->ptr, "s"), + .val = RNA_float_get(op->ptr, "v"), + }; + + if (ED_vpaint_color_transform(obact, vpaint_tx_hsv, &user_data)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_hsv(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint Hue Saturation Value"; - ot->idname = "PAINT_OT_vertex_color_hsv"; - ot->description = "Adjust vertex color HSV values"; - - /* api callbacks */ - ot->exec = vertex_color_hsv_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* params */ - RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); - RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f); - RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f); + /* identifiers */ + ot->name = "Vertex Paint Hue Saturation Value"; + ot->idname = "PAINT_OT_vertex_color_hsv"; + ot->description = "Adjust vertex color HSV values"; + + /* api callbacks */ + ot->exec = vertex_color_hsv_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); + RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f); + RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f); } static void vpaint_tx_invert(const float col[3], const void *UNUSED(user_data), float r_col[3]) { - for (int i = 0; i < 3; i++) { - r_col[i] = 1.0f - col[i]; - } + for (int i = 0; i < 3; i++) { + r_col[i] = 1.0f - col[i]; + } } static int vertex_color_invert_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *obact = CTX_data_active_object(C); - - if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *obact = CTX_data_active_object(C); + + if (ED_vpaint_color_transform(obact, vpaint_tx_invert, NULL)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_invert(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint Invert"; - ot->idname = "PAINT_OT_vertex_color_invert"; - ot->description = "Invert RGB values"; + /* identifiers */ + ot->name = "Vertex Paint Invert"; + ot->idname = "PAINT_OT_vertex_color_invert"; + ot->description = "Invert RGB values"; - /* api callbacks */ - ot->exec = vertex_color_invert_exec; - ot->poll = vertex_paint_mode_poll; + /* api callbacks */ + ot->exec = vertex_color_invert_exec; + ot->poll = vertex_paint_mode_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - struct VPaintTx_LevelsData { - float gain; - float offset; + float gain; + float offset; }; static void vpaint_tx_levels(const float col[3], const void *user_data, float r_col[3]) { - const struct VPaintTx_LevelsData *data = user_data; - for (int i = 0; i < 3; i++) { - r_col[i] = data->gain * (col[i] + data->offset); - } + const struct VPaintTx_LevelsData *data = user_data; + for (int i = 0; i < 3; i++) { + r_col[i] = data->gain * (col[i] + data->offset); + } } static int vertex_color_levels_exec(bContext *C, wmOperator *op) { - Object *obact = CTX_data_active_object(C); - - const struct VPaintTx_LevelsData user_data = { - .gain = RNA_float_get(op->ptr, "gain"), - .offset = RNA_float_get(op->ptr, "offset"), - }; - - if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) { - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + Object *obact = CTX_data_active_object(C); + + const struct VPaintTx_LevelsData user_data = { + .gain = RNA_float_get(op->ptr, "gain"), + .offset = RNA_float_get(op->ptr, "offset"), + }; + + if (ED_vpaint_color_transform(obact, vpaint_tx_levels, &user_data)) { + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obact); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_vertex_color_levels(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Vertex Paint Levels"; - ot->idname = "PAINT_OT_vertex_color_levels"; - ot->description = "Adjust levels of vertex colors"; - - /* api callbacks */ - ot->exec = vertex_color_levels_exec; - ot->poll = vertex_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* params */ - RNA_def_float(ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f); - RNA_def_float(ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f); + /* identifiers */ + ot->name = "Vertex Paint Levels"; + ot->idname = "PAINT_OT_vertex_color_levels"; + ot->description = "Adjust levels of vertex colors"; + + /* api callbacks */ + ot->exec = vertex_color_levels_exec; + ot->poll = vertex_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + RNA_def_float( + ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f); + RNA_def_float( + ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c index 4a5c72e5199..6511c90f5e1 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_utils.c @@ -41,53 +41,50 @@ #include "ED_mesh.h" -#include "paint_intern.h" /* own include */ +#include "paint_intern.h" /* own include */ #define EPS_SATURATION 0.0005f /** * Apply callback to each vertex of the active vertex color layer. */ -bool ED_vpaint_color_transform( - struct Object *ob, - VPaintTransform_Callback vpaint_tx_fn, - const void *user_data) +bool ED_vpaint_color_transform(struct Object *ob, + VPaintTransform_Callback vpaint_tx_fn, + const void *user_data) { - Mesh *me; - const MPoly *mp; + Mesh *me; + const MPoly *mp; - if (((me = BKE_mesh_from_object(ob)) == NULL) || - (ED_mesh_color_ensure(me, NULL) == false)) - { - return false; - } + if (((me = BKE_mesh_from_object(ob)) == NULL) || (ED_mesh_color_ensure(me, NULL) == false)) { + return false; + } - const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; - mp = me->mpoly; + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + mp = me->mpoly; - for (int i = 0; i < me->totpoly; i++, mp++) { - MLoopCol *lcol = &me->mloopcol[mp->loopstart]; + for (int i = 0; i < me->totpoly; i++, mp++) { + MLoopCol *lcol = &me->mloopcol[mp->loopstart]; - if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { - continue; - } + if (use_face_sel && !(mp->flag & ME_FACE_SEL)) { + continue; + } - for (int j = 0; j < mp->totloop; j++, lcol++) { - float col_mix[3]; - rgb_uchar_to_float(col_mix, &lcol->r); + for (int j = 0; j < mp->totloop; j++, lcol++) { + float col_mix[3]; + rgb_uchar_to_float(col_mix, &lcol->r); - vpaint_tx_fn(col_mix, user_data, col_mix); + vpaint_tx_fn(col_mix, user_data, col_mix); - rgb_float_to_uchar(&lcol->r, col_mix); - } - } + rgb_float_to_uchar(&lcol->r, col_mix); + } + } - /* remove stale me->mcol, will be added later */ - BKE_mesh_tessface_clear(me); + /* remove stale me->mcol, will be added later */ + BKE_mesh_tessface_clear(me); - DEG_id_tag_update(&me->id, 0); + DEG_id_tag_update(&me->id, 0); - return true; + return true; } /* -------------------------------------------------------------------- */ @@ -96,548 +93,566 @@ bool ED_vpaint_color_transform( BLI_INLINE uint mcol_blend(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - if (fac >= 255) { - return col_dst; - } + if (fac >= 255) { + return col_dst; + } - mfac = 255 - fac; + mfac = 255 - fac; - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; - /* Updated to use the rgb squared color model which blends nicer. */ - int r1 = cp_src[0] * cp_src[0]; - int g1 = cp_src[1] * cp_src[1]; - int b1 = cp_src[2] * cp_src[2]; - int a1 = cp_src[3] * cp_src[3]; + /* Updated to use the rgb squared color model which blends nicer. */ + int r1 = cp_src[0] * cp_src[0]; + int g1 = cp_src[1] * cp_src[1]; + int b1 = cp_src[2] * cp_src[2]; + int a1 = cp_src[3] * cp_src[3]; - int r2 = cp_dst[0] * cp_dst[0]; - int g2 = cp_dst[1] * cp_dst[1]; - int b2 = cp_dst[2] * cp_dst[2]; - int a2 = cp_dst[3] * cp_dst[3]; + int r2 = cp_dst[0] * cp_dst[0]; + int g2 = cp_dst[1] * cp_dst[1]; + int b2 = cp_dst[2] * cp_dst[2]; + int a2 = cp_dst[3] * cp_dst[3]; - cp_mix[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255))); - cp_mix[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255))); - cp_mix[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255))); - cp_mix[3] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255))); + cp_mix[0] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * r1 + fac * r2), 255))); + cp_mix[1] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * g1 + fac * g2), 255))); + cp_mix[2] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * b1 + fac * b2), 255))); + cp_mix[3] = round_fl_to_uchar(sqrtf(divide_round_i((mfac * a1 + fac * a2), 255))); - return col_mix; + return col_mix; } BLI_INLINE uint mcol_add(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = cp_src[0] + divide_round_i((fac * cp_dst[0]), 255); - cp_mix[0] = (temp > 254) ? 255 : temp; - temp = cp_src[1] + divide_round_i((fac * cp_dst[1]), 255); - cp_mix[1] = (temp > 254) ? 255 : temp; - temp = cp_src[2] + divide_round_i((fac * cp_dst[2]), 255); - cp_mix[2] = (temp > 254) ? 255 : temp; - temp = cp_src[3] + divide_round_i((fac * cp_dst[3]), 255); - cp_mix[3] = (temp > 254) ? 255 : temp; - - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + temp = cp_src[0] + divide_round_i((fac * cp_dst[0]), 255); + cp_mix[0] = (temp > 254) ? 255 : temp; + temp = cp_src[1] + divide_round_i((fac * cp_dst[1]), 255); + cp_mix[1] = (temp > 254) ? 255 : temp; + temp = cp_src[2] + divide_round_i((fac * cp_dst[2]), 255); + cp_mix[2] = (temp > 254) ? 255 : temp; + temp = cp_src[3] + divide_round_i((fac * cp_dst[3]), 255); + cp_mix[3] = (temp > 254) ? 255 : temp; + + return col_mix; } BLI_INLINE uint mcol_sub(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = cp_src[0] - divide_round_i((fac * cp_dst[0]), 255); - cp_mix[0] = (temp < 0) ? 0 : temp; - temp = cp_src[1] - divide_round_i((fac * cp_dst[1]), 255); - cp_mix[1] = (temp < 0) ? 0 : temp; - temp = cp_src[2] - divide_round_i((fac * cp_dst[2]), 255); - cp_mix[2] = (temp < 0) ? 0 : temp; - temp = cp_src[3] - divide_round_i((fac * cp_dst[3]), 255); - cp_mix[3] = (temp < 0) ? 0 : temp; - - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + temp = cp_src[0] - divide_round_i((fac * cp_dst[0]), 255); + cp_mix[0] = (temp < 0) ? 0 : temp; + temp = cp_src[1] - divide_round_i((fac * cp_dst[1]), 255); + cp_mix[1] = (temp < 0) ? 0 : temp; + temp = cp_src[2] - divide_round_i((fac * cp_dst[2]), 255); + cp_mix[2] = (temp < 0) ? 0 : temp; + temp = cp_src[3] - divide_round_i((fac * cp_dst[3]), 255); + cp_mix[3] = (temp < 0) ? 0 : temp; + + return col_mix; } BLI_INLINE uint mcol_mul(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - mfac = 255 - fac; + mfac = 255 - fac; - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; - /* first mul, then blend the fac */ - cp_mix[0] = divide_round_i(mfac * cp_src[0] * 255 + fac * cp_dst[0] * cp_src[0], 255 * 255); - cp_mix[1] = divide_round_i(mfac * cp_src[1] * 255 + fac * cp_dst[1] * cp_src[1], 255 * 255); - cp_mix[2] = divide_round_i(mfac * cp_src[2] * 255 + fac * cp_dst[2] * cp_src[2], 255 * 255); - cp_mix[3] = divide_round_i(mfac * cp_src[3] * 255 + fac * cp_dst[3] * cp_src[3], 255 * 255); + /* first mul, then blend the fac */ + cp_mix[0] = divide_round_i(mfac * cp_src[0] * 255 + fac * cp_dst[0] * cp_src[0], 255 * 255); + cp_mix[1] = divide_round_i(mfac * cp_src[1] * 255 + fac * cp_dst[1] * cp_src[1], 255 * 255); + cp_mix[2] = divide_round_i(mfac * cp_src[2] * 255 + fac * cp_dst[2] * cp_src[2], 255 * 255); + cp_mix[3] = divide_round_i(mfac * cp_src[3] * 255 + fac * cp_dst[3] * cp_src[3], 255 * 255); - return col_mix; + return col_mix; } BLI_INLINE uint mcol_lighten(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - else if (fac >= 255) { - return col_dst; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - /* See if are lighter, if so mix, else don't do anything. - * if the paint color is darker then the original, then ignore */ - if (IMB_colormanagement_get_luminance_byte(cp_src) > IMB_colormanagement_get_luminance_byte(cp_dst)) { - return col_src; - } - - cp_mix[0] = divide_round_i(mfac * cp_src[0] + fac * cp_dst[0], 255); - cp_mix[1] = divide_round_i(mfac * cp_src[1] + fac * cp_dst[1], 255); - cp_mix[2] = divide_round_i(mfac * cp_src[2] + fac * cp_dst[2], 255); - cp_mix[3] = divide_round_i(mfac * cp_src[3] + fac * cp_dst[3], 255); - - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + else if (fac >= 255) { + return col_dst; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + /* See if are lighter, if so mix, else don't do anything. + * if the paint color is darker then the original, then ignore */ + if (IMB_colormanagement_get_luminance_byte(cp_src) > + IMB_colormanagement_get_luminance_byte(cp_dst)) { + return col_src; + } + + cp_mix[0] = divide_round_i(mfac * cp_src[0] + fac * cp_dst[0], 255); + cp_mix[1] = divide_round_i(mfac * cp_src[1] + fac * cp_dst[1], 255); + cp_mix[2] = divide_round_i(mfac * cp_src[2] + fac * cp_dst[2], 255); + cp_mix[3] = divide_round_i(mfac * cp_src[3] + fac * cp_dst[3], 255); + + return col_mix; } BLI_INLINE uint mcol_darken(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - else if (fac >= 255) { - return col_dst; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - /* See if were darker, if so mix, else don't do anything. - * if the paint color is brighter then the original, then ignore */ - if (IMB_colormanagement_get_luminance_byte(cp_src) < IMB_colormanagement_get_luminance_byte(cp_dst)) { - return col_src; - } - - cp_mix[0] = divide_round_i((mfac * cp_src[0] + fac * cp_dst[0]), 255); - cp_mix[1] = divide_round_i((mfac * cp_src[1] + fac * cp_dst[1]), 255); - cp_mix[2] = divide_round_i((mfac * cp_src[2] + fac * cp_dst[2]), 255); - cp_mix[3] = divide_round_i((mfac * cp_src[3] + fac * cp_dst[3]), 255); - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + else if (fac >= 255) { + return col_dst; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + /* See if were darker, if so mix, else don't do anything. + * if the paint color is brighter then the original, then ignore */ + if (IMB_colormanagement_get_luminance_byte(cp_src) < + IMB_colormanagement_get_luminance_byte(cp_dst)) { + return col_src; + } + + cp_mix[0] = divide_round_i((mfac * cp_src[0] + fac * cp_dst[0]), 255); + cp_mix[1] = divide_round_i((mfac * cp_src[1] + fac * cp_dst[1]), 255); + cp_mix[2] = divide_round_i((mfac * cp_src[2] + fac * cp_dst[2]), 255); + cp_mix[3] = divide_round_i((mfac * cp_src[3] + fac * cp_dst[3]), 255); + return col_mix; } BLI_INLINE uint mcol_colordodge(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = (cp_dst[0] == 255) ? 255 : min_ii((cp_src[0] * 225) / (255 - cp_dst[0]), 255); - cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; - temp = (cp_dst[1] == 255) ? 255 : min_ii((cp_src[1] * 225) / (255 - cp_dst[1]), 255); - cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; - temp = (cp_dst[2] == 255) ? 255 : min_ii((cp_src[2] * 225) / (255 - cp_dst[2]), 255); - cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; - temp = (cp_dst[3] == 255) ? 255 : min_ii((cp_src[3] * 225) / (255 - cp_dst[3]), 255); - cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + temp = (cp_dst[0] == 255) ? 255 : min_ii((cp_src[0] * 225) / (255 - cp_dst[0]), 255); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; + temp = (cp_dst[1] == 255) ? 255 : min_ii((cp_src[1] * 225) / (255 - cp_dst[1]), 255); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; + temp = (cp_dst[2] == 255) ? 255 : min_ii((cp_src[2] * 225) / (255 - cp_dst[2]), 255); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; + temp = (cp_dst[3] == 255) ? 255 : min_ii((cp_src[3] * 225) / (255 - cp_dst[3]), 255); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; + return col_mix; } BLI_INLINE uint mcol_difference(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = abs(cp_src[0] - cp_dst[0]); - cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; - temp = abs(cp_src[1] - cp_dst[1]); - cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; - temp = abs(cp_src[2] - cp_dst[2]); - cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; - temp = abs(cp_src[3] - cp_dst[3]); - cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + temp = abs(cp_src[0] - cp_dst[0]); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; + temp = abs(cp_src[1] - cp_dst[1]); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; + temp = abs(cp_src[2] - cp_dst[2]); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; + temp = abs(cp_src[3] - cp_dst[3]); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; + return col_mix; } BLI_INLINE uint mcol_screen(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - temp = max_ii(255 - (((255 - cp_src[0]) * (255 - cp_dst[0])) / 255), 0); - cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; - temp = max_ii(255 - (((255 - cp_src[1]) * (255 - cp_dst[1])) / 255), 0); - cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; - temp = max_ii(255 - (((255 - cp_src[2]) * (255 - cp_dst[2])) / 255), 0); - cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; - temp = max_ii(255 - (((255 - cp_src[3]) * (255 - cp_dst[3])) / 255), 0); - cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + temp = max_ii(255 - (((255 - cp_src[0]) * (255 - cp_dst[0])) / 255), 0); + cp_mix[0] = (mfac * cp_src[0] + temp * fac) / 255; + temp = max_ii(255 - (((255 - cp_src[1]) * (255 - cp_dst[1])) / 255), 0); + cp_mix[1] = (mfac * cp_src[1] + temp * fac) / 255; + temp = max_ii(255 - (((255 - cp_src[2]) * (255 - cp_dst[2])) / 255), 0); + cp_mix[2] = (mfac * cp_src[2] + temp * fac) / 255; + temp = max_ii(255 - (((255 - cp_src[3]) * (255 - cp_dst[3])) / 255), 0); + cp_mix[3] = (mfac * cp_src[3] + temp * fac) / 255; + return col_mix; } BLI_INLINE uint mcol_hardlight(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - int i = 0; - - for (i = 0; i < 4; i++) { - if (cp_dst[i] > 127) { - temp = 255 - ((255 - 2 * (cp_dst[i] - 127)) * (255 - cp_src[i]) / 255); - } - else { - temp = (2 * cp_dst[i] * cp_src[i]) >> 8; - } - cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255); - } - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_dst[i] > 127) { + temp = 255 - ((255 - 2 * (cp_dst[i] - 127)) * (255 - cp_src[i]) / 255); + } + else { + temp = (2 * cp_dst[i] * cp_src[i]) >> 8; + } + cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255); + } + return col_mix; } BLI_INLINE uint mcol_overlay(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - int i = 0; - - for (i = 0; i < 4; i++) { - if (cp_src[i] > 127) { - temp = 255 - ((255 - 2 * (cp_src[i] - 127)) * (255 - cp_dst[i]) / 255); - } - else { - temp = (2 * cp_dst[i] * cp_src[i]) >> 8; - } - cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255); - } - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_src[i] > 127) { + temp = 255 - ((255 - 2 * (cp_src[i] - 127)) * (255 - cp_dst[i]) / 255); + } + else { + temp = (2 * cp_dst[i] * cp_src[i]) >> 8; + } + cp_mix[i] = min_ii((mfac * cp_src[i] + temp * fac) / 255, 255); + } + return col_mix; } BLI_INLINE uint mcol_softlight(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; - - if (fac == 0) { - return col_src; - } - - mfac = 255 - fac; - - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; - - int i = 0; - - for (i = 0; i < 4; i++) { - if (cp_src[i] < 127) { - temp = ((2 * ((cp_dst[i] / 2) + 64)) * cp_src[i]) / 255; - } - else { - temp = 255 - (2 * (255 - ((cp_dst[i] / 2) + 64)) * (255 - cp_src[i]) / 255); - } - cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255; - } - return col_mix; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; + + if (fac == 0) { + return col_src; + } + + mfac = 255 - fac; + + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; + + int i = 0; + + for (i = 0; i < 4; i++) { + if (cp_src[i] < 127) { + temp = ((2 * ((cp_dst[i] / 2) + 64)) * cp_src[i]) / 255; + } + else { + temp = 255 - (2 * (255 - ((cp_dst[i] / 2) + 64)) * (255 - cp_src[i]) / 255); + } + cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255; + } + return col_mix; } BLI_INLINE uint mcol_exclusion(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac, temp; - uint col_mix = 0; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac, temp; + uint col_mix = 0; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - mfac = 255 - fac; + mfac = 255 - fac; - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; - int i = 0; + int i = 0; - for (i = 0; i < 4; i++) { - temp = 127 - ((2 * (cp_src[i] - 127) * (cp_dst[i] - 127)) / 255); - cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255; - } - return col_mix; + for (i = 0; i < 4; i++) { + temp = 127 - ((2 * (cp_src[i] - 127) * (cp_dst[i] - 127)) / 255); + cp_mix[i] = (temp * fac + cp_src[i] * mfac) / 255; + } + return col_mix; } BLI_INLINE uint mcol_luminosity(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - mfac = 255 - fac; + mfac = 255 - fac; - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; - float h1, s1, v1; - float h2, s2, v2; - float r, g, b; - rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); - rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); - v1 = v2; + v1 = v2; - hsv_to_rgb(h1, s1, v1, &r, &g, &b); + hsv_to_rgb(h1, s1, v1, &r, &g, &b); - cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; - cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; - cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; - cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255; - return col_mix; + cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; + cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; + cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; + cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255; + return col_mix; } BLI_INLINE uint mcol_saturation(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - mfac = 255 - fac; + mfac = 255 - fac; - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; - float h1, s1, v1; - float h2, s2, v2; - float r, g, b; - rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); - rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); - if (s1 > EPS_SATURATION) { - s1 = s2; - } + if (s1 > EPS_SATURATION) { + s1 = s2; + } - hsv_to_rgb(h1, s1, v1, &r, &g, &b); + hsv_to_rgb(h1, s1, v1, &r, &g, &b); - cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; - cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; - cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; - return col_mix; + cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; + cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; + cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; + return col_mix; } BLI_INLINE uint mcol_hue(uint col_src, uint col_dst, int fac) { - uchar *cp_src, *cp_dst, *cp_mix; - int mfac; - uint col_mix = 0; + uchar *cp_src, *cp_dst, *cp_mix; + int mfac; + uint col_mix = 0; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - mfac = 255 - fac; + mfac = 255 - fac; - cp_src = (uchar *)&col_src; - cp_dst = (uchar *)&col_dst; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_dst = (uchar *)&col_dst; + cp_mix = (uchar *)&col_mix; - float h1, s1, v1; - float h2, s2, v2; - float r, g, b; - rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); - rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); + float h1, s1, v1; + float h2, s2, v2; + float r, g, b; + rgb_to_hsv(cp_src[0] / 255.0f, cp_src[1] / 255.0f, cp_src[2] / 255.0f, &h1, &s1, &v1); + rgb_to_hsv(cp_dst[0] / 255.0f, cp_dst[1] / 255.0f, cp_dst[2] / 255.0f, &h2, &s2, &v2); - h1 = h2; + h1 = h2; - hsv_to_rgb(h1, s1, v1, &r, &g, &b); + hsv_to_rgb(h1, s1, v1, &r, &g, &b); - cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; - cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; - cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; - cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255; - return col_mix; + cp_mix[0] = ((int)(r * 255.0f) * fac + mfac * cp_src[0]) / 255; + cp_mix[1] = ((int)(g * 255.0f) * fac + mfac * cp_src[1]) / 255; + cp_mix[2] = ((int)(b * 255.0f) * fac + mfac * cp_src[2]) / 255; + cp_mix[3] = ((int)(cp_dst[3]) * fac + mfac * cp_src[3]) / 255; + return col_mix; } BLI_INLINE uint mcol_alpha_add(uint col_src, int fac) { - uchar *cp_src, *cp_mix; - int temp; - uint col_mix = col_src; + uchar *cp_src, *cp_mix; + int temp; + uint col_mix = col_src; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - cp_src = (uchar *)&col_src; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_mix = (uchar *)&col_mix; - temp = cp_src[3] + fac; - cp_mix[3] = (temp > 254) ? 255 : temp; + temp = cp_src[3] + fac; + cp_mix[3] = (temp > 254) ? 255 : temp; - return col_mix; + return col_mix; } BLI_INLINE uint mcol_alpha_sub(uint col_src, int fac) { - uchar *cp_src, *cp_mix; - int temp; - uint col_mix = col_src; + uchar *cp_src, *cp_mix; + int temp; + uint col_mix = col_src; - if (fac == 0) { - return col_src; - } + if (fac == 0) { + return col_src; + } - cp_src = (uchar *)&col_src; - cp_mix = (uchar *)&col_mix; + cp_src = (uchar *)&col_src; + cp_mix = (uchar *)&col_mix; - temp = cp_src[3] - fac; - cp_mix[3] = temp < 0 ? 0 : temp; + temp = cp_src[3] - fac; + cp_mix[3] = temp < 0 ? 0 : temp; - return col_mix; + return col_mix; } /* wpaint has 'ED_wpaint_blend_tool' */ -uint ED_vpaint_blend_tool( - const int tool, const uint col, - const uint paintcol, const int alpha_i) +uint ED_vpaint_blend_tool(const int tool, const uint col, const uint paintcol, const int alpha_i) { - switch ((IMB_BlendMode)tool) { - case IMB_BLEND_MIX: return mcol_blend(col, paintcol, alpha_i); - case IMB_BLEND_ADD: return mcol_add(col, paintcol, alpha_i); - case IMB_BLEND_SUB: return mcol_sub(col, paintcol, alpha_i); - case IMB_BLEND_MUL: return mcol_mul(col, paintcol, alpha_i); - case IMB_BLEND_LIGHTEN: return mcol_lighten(col, paintcol, alpha_i); - case IMB_BLEND_DARKEN: return mcol_darken(col, paintcol, alpha_i); - case IMB_BLEND_COLORDODGE: return mcol_colordodge(col, paintcol, alpha_i); - case IMB_BLEND_DIFFERENCE: return mcol_difference(col, paintcol, alpha_i); - case IMB_BLEND_SCREEN: return mcol_screen(col, paintcol, alpha_i); - case IMB_BLEND_HARDLIGHT: return mcol_hardlight(col, paintcol, alpha_i); - case IMB_BLEND_OVERLAY: return mcol_overlay(col, paintcol, alpha_i); - case IMB_BLEND_SOFTLIGHT: return mcol_softlight(col, paintcol, alpha_i); - case IMB_BLEND_EXCLUSION: return mcol_exclusion(col, paintcol, alpha_i); - case IMB_BLEND_LUMINOSITY: return mcol_luminosity(col, paintcol, alpha_i); - case IMB_BLEND_SATURATION: return mcol_saturation(col, paintcol, alpha_i); - case IMB_BLEND_HUE: return mcol_hue(col, paintcol, alpha_i); - /* non-color */ - case IMB_BLEND_ERASE_ALPHA: return mcol_alpha_sub(col, alpha_i); - case IMB_BLEND_ADD_ALPHA: return mcol_alpha_add(col, alpha_i); - default: - BLI_assert(0); - return 0; - } + switch ((IMB_BlendMode)tool) { + case IMB_BLEND_MIX: + return mcol_blend(col, paintcol, alpha_i); + case IMB_BLEND_ADD: + return mcol_add(col, paintcol, alpha_i); + case IMB_BLEND_SUB: + return mcol_sub(col, paintcol, alpha_i); + case IMB_BLEND_MUL: + return mcol_mul(col, paintcol, alpha_i); + case IMB_BLEND_LIGHTEN: + return mcol_lighten(col, paintcol, alpha_i); + case IMB_BLEND_DARKEN: + return mcol_darken(col, paintcol, alpha_i); + case IMB_BLEND_COLORDODGE: + return mcol_colordodge(col, paintcol, alpha_i); + case IMB_BLEND_DIFFERENCE: + return mcol_difference(col, paintcol, alpha_i); + case IMB_BLEND_SCREEN: + return mcol_screen(col, paintcol, alpha_i); + case IMB_BLEND_HARDLIGHT: + return mcol_hardlight(col, paintcol, alpha_i); + case IMB_BLEND_OVERLAY: + return mcol_overlay(col, paintcol, alpha_i); + case IMB_BLEND_SOFTLIGHT: + return mcol_softlight(col, paintcol, alpha_i); + case IMB_BLEND_EXCLUSION: + return mcol_exclusion(col, paintcol, alpha_i); + case IMB_BLEND_LUMINOSITY: + return mcol_luminosity(col, paintcol, alpha_i); + case IMB_BLEND_SATURATION: + return mcol_saturation(col, paintcol, alpha_i); + case IMB_BLEND_HUE: + return mcol_hue(col, paintcol, alpha_i); + /* non-color */ + case IMB_BLEND_ERASE_ALPHA: + return mcol_alpha_sub(col, alpha_i); + case IMB_BLEND_ADD_ALPHA: + return mcol_alpha_add(col, alpha_i); + default: + BLI_assert(0); + return 0; + } } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_proj.c b/source/blender/editors/sculpt_paint/paint_vertex_proj.c index 9246c852b3a..7bca7745ca0 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_proj.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_proj.c @@ -43,195 +43,193 @@ #include "ED_screen.h" #include "ED_view3d.h" -#include "paint_intern.h" /* own include */ - +#include "paint_intern.h" /* own include */ /* Opaque Structs for internal use */ /* stored while painting */ struct VertProjHandle { - CoNo *vcosnos; + CoNo *vcosnos; - bool use_update; + bool use_update; - /* use for update */ - float *dists_sq; + /* use for update */ + float *dists_sq; - Object *ob; - Scene *scene; + Object *ob; + Scene *scene; }; /* only for passing to the callbacks */ struct VertProjUpdate { - struct VertProjHandle *vp_handle; + struct VertProjHandle *vp_handle; - /* runtime */ - ARegion *ar; - const float *mval_fl; + /* runtime */ + ARegion *ar; + const float *mval_fl; }; - /* -------------------------------------------------------------------- */ /* Internal Init */ static void vpaint_proj_dm_map_cosnos_init__map_cb( - void *userData, int index, const float co[3], - const float no_f[3], const short no_s[3]) + void *userData, int index, const float co[3], const float no_f[3], const short no_s[3]) { - struct VertProjHandle *vp_handle = userData; - CoNo *co_no = &vp_handle->vcosnos[index]; - - /* check if we've been here before (normal should not be 0) */ - if (!is_zero_v3(co_no->no)) { - /* remember that multiple dm verts share the same source vert */ - vp_handle->use_update = true; - return; - } - - copy_v3_v3(co_no->co, co); - if (no_f) { - copy_v3_v3(co_no->no, no_f); - } - else { - normal_short_to_float_v3(co_no->no, no_s); - } + struct VertProjHandle *vp_handle = userData; + CoNo *co_no = &vp_handle->vcosnos[index]; + + /* check if we've been here before (normal should not be 0) */ + if (!is_zero_v3(co_no->no)) { + /* remember that multiple dm verts share the same source vert */ + vp_handle->use_update = true; + return; + } + + copy_v3_v3(co_no->co, co); + if (no_f) { + copy_v3_v3(co_no->no, no_f); + } + else { + normal_short_to_float_v3(co_no->no, no_s); + } } -static void vpaint_proj_dm_map_cosnos_init( - struct Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob, - struct VertProjHandle *vp_handle) +static void vpaint_proj_dm_map_cosnos_init(struct Depsgraph *depsgraph, + Scene *UNUSED(scene), + Object *ob, + struct VertProjHandle *vp_handle) { - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *me = ob->data; + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *me = ob->data; - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH_ORIGINDEX; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH_ORIGINDEX; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); - memset(vp_handle->vcosnos, 0, sizeof(*vp_handle->vcosnos) * me->totvert); - BKE_mesh_foreach_mapped_vert(me_eval, vpaint_proj_dm_map_cosnos_init__map_cb, vp_handle, MESH_FOREACH_USE_NORMAL); + memset(vp_handle->vcosnos, 0, sizeof(*vp_handle->vcosnos) * me->totvert); + BKE_mesh_foreach_mapped_vert( + me_eval, vpaint_proj_dm_map_cosnos_init__map_cb, vp_handle, MESH_FOREACH_USE_NORMAL); } - /* -------------------------------------------------------------------- */ /* Internal Update */ /* Same as init but take mouse location into account */ static void vpaint_proj_dm_map_cosnos_update__map_cb( - void *userData, int index, const float co[3], - const float no_f[3], const short no_s[3]) + void *userData, int index, const float co[3], const float no_f[3], const short no_s[3]) { - struct VertProjUpdate *vp_update = userData; - struct VertProjHandle *vp_handle = vp_update->vp_handle; - - CoNo *co_no = &vp_handle->vcosnos[index]; - - /* find closest vertex */ - { - /* first find distance to this vertex */ - float co_ss[2]; /* screenspace */ - - if (ED_view3d_project_float_object( - vp_update->ar, - co, co_ss, - V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) - { - const float dist_sq = len_squared_v2v2(vp_update->mval_fl, co_ss); - if (dist_sq > vp_handle->dists_sq[index]) { - /* bail out! */ - return; - } - - vp_handle->dists_sq[index] = dist_sq; - } - else if (vp_handle->dists_sq[index] != FLT_MAX) { - /* already initialized & couldn't project this 'co' */ - return; - } - } - /* continue with regular functionality */ - - copy_v3_v3(co_no->co, co); - if (no_f) { - copy_v3_v3(co_no->no, no_f); - } - else { - normal_short_to_float_v3(co_no->no, no_s); - } + struct VertProjUpdate *vp_update = userData; + struct VertProjHandle *vp_handle = vp_update->vp_handle; + + CoNo *co_no = &vp_handle->vcosnos[index]; + + /* find closest vertex */ + { + /* first find distance to this vertex */ + float co_ss[2]; /* screenspace */ + + if (ED_view3d_project_float_object( + vp_update->ar, co, co_ss, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == + V3D_PROJ_RET_OK) { + const float dist_sq = len_squared_v2v2(vp_update->mval_fl, co_ss); + if (dist_sq > vp_handle->dists_sq[index]) { + /* bail out! */ + return; + } + + vp_handle->dists_sq[index] = dist_sq; + } + else if (vp_handle->dists_sq[index] != FLT_MAX) { + /* already initialized & couldn't project this 'co' */ + return; + } + } + /* continue with regular functionality */ + + copy_v3_v3(co_no->co, co); + if (no_f) { + copy_v3_v3(co_no->no, no_f); + } + else { + normal_short_to_float_v3(co_no->no, no_s); + } } -static void vpaint_proj_dm_map_cosnos_update( - struct Depsgraph *depsgraph, struct VertProjHandle *vp_handle, - ARegion *ar, const float mval_fl[2]) +static void vpaint_proj_dm_map_cosnos_update(struct Depsgraph *depsgraph, + struct VertProjHandle *vp_handle, + ARegion *ar, + const float mval_fl[2]) { - struct VertProjUpdate vp_update = {vp_handle, ar, mval_fl}; + struct VertProjUpdate vp_update = {vp_handle, ar, mval_fl}; - Object *ob = vp_handle->ob; - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *me = ob->data; + Object *ob = vp_handle->ob; + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *me = ob->data; - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH_ORIGINDEX; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH_ORIGINDEX; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); - /* quick sanity check - we shouldn't have to run this if there are no modifiers */ - BLI_assert(BLI_listbase_is_empty(&ob->modifiers) == false); + /* quick sanity check - we shouldn't have to run this if there are no modifiers */ + BLI_assert(BLI_listbase_is_empty(&ob->modifiers) == false); - copy_vn_fl(vp_handle->dists_sq, me->totvert, FLT_MAX); - BKE_mesh_foreach_mapped_vert(me_eval, vpaint_proj_dm_map_cosnos_update__map_cb, &vp_update, MESH_FOREACH_USE_NORMAL); + copy_vn_fl(vp_handle->dists_sq, me->totvert, FLT_MAX); + BKE_mesh_foreach_mapped_vert( + me_eval, vpaint_proj_dm_map_cosnos_update__map_cb, &vp_update, MESH_FOREACH_USE_NORMAL); } - /* -------------------------------------------------------------------- */ /* Public Functions */ -struct VertProjHandle *ED_vpaint_proj_handle_create( - struct Depsgraph *depsgraph, Scene *scene, Object *ob, - CoNo **r_vcosnos) +struct VertProjHandle *ED_vpaint_proj_handle_create(struct Depsgraph *depsgraph, + Scene *scene, + Object *ob, + CoNo **r_vcosnos) { - struct VertProjHandle *vp_handle = MEM_mallocN(sizeof(struct VertProjHandle), __func__); - Mesh *me = ob->data; + struct VertProjHandle *vp_handle = MEM_mallocN(sizeof(struct VertProjHandle), __func__); + Mesh *me = ob->data; - /* setup the handle */ - vp_handle->vcosnos = MEM_mallocN(sizeof(CoNo) * me->totvert, "vertexcosnos map"); - vp_handle->use_update = false; + /* setup the handle */ + vp_handle->vcosnos = MEM_mallocN(sizeof(CoNo) * me->totvert, "vertexcosnos map"); + vp_handle->use_update = false; - /* sets 'use_update' if needed */ - vpaint_proj_dm_map_cosnos_init(depsgraph, scene, ob, vp_handle); + /* sets 'use_update' if needed */ + vpaint_proj_dm_map_cosnos_init(depsgraph, scene, ob, vp_handle); - if (vp_handle->use_update) { - vp_handle->dists_sq = MEM_mallocN(sizeof(float) * me->totvert, __func__); + if (vp_handle->use_update) { + vp_handle->dists_sq = MEM_mallocN(sizeof(float) * me->totvert, __func__); - vp_handle->ob = ob; - vp_handle->scene = scene; - } - else { - vp_handle->dists_sq = NULL; + vp_handle->ob = ob; + vp_handle->scene = scene; + } + else { + vp_handle->dists_sq = NULL; - vp_handle->ob = NULL; - vp_handle->scene = NULL; - } + vp_handle->ob = NULL; + vp_handle->scene = NULL; + } - *r_vcosnos = vp_handle->vcosnos; - return vp_handle; + *r_vcosnos = vp_handle->vcosnos; + return vp_handle; } -void ED_vpaint_proj_handle_update( - struct Depsgraph *depsgraph, struct VertProjHandle *vp_handle, - ARegion *ar, const float mval_fl[2]) +void ED_vpaint_proj_handle_update(struct Depsgraph *depsgraph, + struct VertProjHandle *vp_handle, + ARegion *ar, + const float mval_fl[2]) { - if (vp_handle->use_update) { - vpaint_proj_dm_map_cosnos_update(depsgraph, vp_handle, ar, mval_fl); - } + if (vp_handle->use_update) { + vpaint_proj_dm_map_cosnos_update(depsgraph, vp_handle, ar, mval_fl); + } } -void ED_vpaint_proj_handle_free(struct VertProjHandle *vp_handle) +void ED_vpaint_proj_handle_free(struct VertProjHandle *vp_handle) { - if (vp_handle->use_update) { - MEM_freeN(vp_handle->dists_sq); - } + if (vp_handle->use_update) { + MEM_freeN(vp_handle->dists_sq); + } - MEM_freeN(vp_handle->vcosnos); - MEM_freeN(vp_handle); + MEM_freeN(vp_handle->vcosnos); + MEM_freeN(vp_handle); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index cd366ecff3b..29d150c44fe 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -63,7 +63,7 @@ #include "ED_screen.h" #include "ED_view3d.h" -#include "paint_intern.h" /* own include */ +#include "paint_intern.h" /* own include */ /* -------------------------------------------------------------------- */ /** \name Store Previous Weights @@ -72,37 +72,36 @@ * \{ */ struct WPaintPrev { - /* previous vertex weights */ - struct MDeformVert *wpaint_prev; - /* allocation size of prev buffers */ - int tot; + /* previous vertex weights */ + struct MDeformVert *wpaint_prev; + /* allocation size of prev buffers */ + int tot; }; - static void wpaint_prev_init(struct WPaintPrev *wpp) { - wpp->wpaint_prev = NULL; - wpp->tot = 0; + wpp->wpaint_prev = NULL; + wpp->tot = 0; } static void wpaint_prev_create(struct WPaintPrev *wpp, MDeformVert *dverts, int dcount) { - wpaint_prev_init(wpp); + wpaint_prev_init(wpp); - if (dverts && dcount) { - wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev"); - wpp->tot = dcount; - BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount); - } + if (dverts && dcount) { + wpp->wpaint_prev = MEM_mallocN(sizeof(MDeformVert) * dcount, "wpaint prev"); + wpp->tot = dcount; + BKE_defvert_array_copy(wpp->wpaint_prev, dverts, dcount); + } } static void wpaint_prev_destroy(struct WPaintPrev *wpp) { - if (wpp->wpaint_prev) { - BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot); - } - wpp->wpaint_prev = NULL; - wpp->tot = 0; + if (wpp->wpaint_prev) { + BKE_defvert_array_free(wpp->wpaint_prev, wpp->tot); + } + wpp->wpaint_prev = NULL; + wpp->tot = 0; } /** \} */ @@ -113,53 +112,59 @@ static void wpaint_prev_destroy(struct WPaintPrev *wpp) static bool weight_from_bones_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob)); + return (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && modifiers_isDeformedByArmature(ob)); } static int weight_from_bones_exec(bContext *C, wmOperator *op) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - Object *armob = modifiers_isDeformedByArmature(ob); - Mesh *me = ob->data; - int type = RNA_enum_get(op->ptr, "type"); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Object *armob = modifiers_isDeformedByArmature(ob); + Mesh *me = ob->data; + int type = RNA_enum_get(op->ptr, "type"); - ED_object_vgroup_calc_from_armature(op->reports, depsgraph, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X)); + ED_object_vgroup_calc_from_armature( + op->reports, depsgraph, scene, ob, armob, type, (me->editflag & ME_EDIT_MIRROR_X)); - DEG_id_tag_update(&me->id, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); + DEG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void PAINT_OT_weight_from_bones(wmOperatorType *ot) { - static const EnumPropertyItem type_items[] = { - {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"}, - {ARM_GROUPS_ENVELOPE, "ENVELOPES", 0, "From Envelopes", "Weights from envelopes with user defined radius"}, - {0, NULL, 0, NULL, NULL}, - }; - - /* identifiers */ - ot->name = "Weight from Bones"; - ot->idname = "PAINT_OT_weight_from_bones"; - ot->description = ( - "Set the weights of the groups matching the attached armature's selected bones, " - "using the distance between the vertices and the bones"); - - /* api callbacks */ - ot->exec = weight_from_bones_exec; - ot->invoke = WM_menu_invoke; - ot->poll = weight_from_bones_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights"); + static const EnumPropertyItem type_items[] = { + {ARM_GROUPS_AUTO, "AUTOMATIC", 0, "Automatic", "Automatic weights from bones"}, + {ARM_GROUPS_ENVELOPE, + "ENVELOPES", + 0, + "From Envelopes", + "Weights from envelopes with user defined radius"}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Weight from Bones"; + ot->idname = "PAINT_OT_weight_from_bones"; + ot->description = + ("Set the weights of the groups matching the attached armature's selected bones, " + "using the distance between the vertices and the bones"); + + /* api callbacks */ + ot->exec = weight_from_bones_exec; + ot->invoke = WM_menu_invoke; + ot->poll = weight_from_bones_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum( + ot->srna, "type", type_items, 0, "Type", "Method to use for assigning weights"); } /** \} */ @@ -172,95 +177,103 @@ void PAINT_OT_weight_from_bones(wmOperatorType *ot) /* note: we cant sample frontbuf, weight colors are interpolated too unpredictable */ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - ViewContext vc; - Mesh *me; - bool changed = false; - - ED_view3d_viewcontext_init(C, &vc); - me = BKE_mesh_from_object(vc.obact); - - if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) { - const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - int v_idx_best = -1; - uint index; - - view3d_operator_needs_opengl(C); - ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d); - - if (use_vert_sel) { - if (ED_mesh_pick_vert(C, vc.obact, event->mval, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index)) { - v_idx_best = index; - } - } - else { - if (ED_mesh_pick_face_vert(C, vc.obact, event->mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { - v_idx_best = index; - } - else if (ED_mesh_pick_face(C, vc.obact, event->mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { - /* this relies on knowning the internal worksings of ED_mesh_pick_face_vert() */ - BKE_report(op->reports, RPT_WARNING, "The modifier used does not support deformed locations"); - } - } - - if (v_idx_best != -1) { /* should always be valid */ - ToolSettings *ts = vc.scene->toolsettings; - Brush *brush = BKE_paint_brush(&ts->wpaint->paint); - const int vgroup_active = vc.obact->actdef - 1; - float vgroup_weight = defvert_find_weight(&me->dvert[v_idx_best], vgroup_active); - - /* use combined weight in multipaint mode, - * since that's what is displayed to the user in the colors */ - if (ts->multipaint) { - int defbase_tot_sel; - const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); - bool *defbase_sel = BKE_object_defgroup_selected_get(vc.obact, defbase_tot, &defbase_tot_sel); - - if (defbase_tot_sel > 1) { - if (me->editflag & ME_EDIT_MIRROR_X) { - BKE_object_defgroup_mirror_selection( - vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel); - } - - vgroup_weight = BKE_defvert_multipaint_collective_weight( - &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, ts->auto_normalize); - - /* If auto-normalize is enabled, but weights are not normalized, - * the value can exceed 1. */ - CLAMP(vgroup_weight, 0.0f, 1.0f); - } - - MEM_freeN(defbase_sel); - } - - BKE_brush_weight_set(vc.scene, brush, vgroup_weight); - changed = true; - } - } - - if (changed) { - /* not really correct since the brush didn't change, but redraws the toolbar */ - WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); /* ts->wpaint->paint.brush */ - - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + ViewContext vc; + Mesh *me; + bool changed = false; + + ED_view3d_viewcontext_init(C, &vc); + me = BKE_mesh_from_object(vc.obact); + + if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) { + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + int v_idx_best = -1; + uint index; + + view3d_operator_needs_opengl(C); + ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d); + + if (use_vert_sel) { + if (ED_mesh_pick_vert( + C, vc.obact, event->mval, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index)) { + v_idx_best = index; + } + } + else { + if (ED_mesh_pick_face_vert( + C, vc.obact, event->mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { + v_idx_best = index; + } + else if (ED_mesh_pick_face( + C, vc.obact, event->mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { + /* this relies on knowning the internal worksings of ED_mesh_pick_face_vert() */ + BKE_report( + op->reports, RPT_WARNING, "The modifier used does not support deformed locations"); + } + } + + if (v_idx_best != -1) { /* should always be valid */ + ToolSettings *ts = vc.scene->toolsettings; + Brush *brush = BKE_paint_brush(&ts->wpaint->paint); + const int vgroup_active = vc.obact->actdef - 1; + float vgroup_weight = defvert_find_weight(&me->dvert[v_idx_best], vgroup_active); + + /* use combined weight in multipaint mode, + * since that's what is displayed to the user in the colors */ + if (ts->multipaint) { + int defbase_tot_sel; + const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); + bool *defbase_sel = BKE_object_defgroup_selected_get( + vc.obact, defbase_tot, &defbase_tot_sel); + + if (defbase_tot_sel > 1) { + if (me->editflag & ME_EDIT_MIRROR_X) { + BKE_object_defgroup_mirror_selection( + vc.obact, defbase_tot, defbase_sel, defbase_sel, &defbase_tot_sel); + } + + vgroup_weight = BKE_defvert_multipaint_collective_weight(&me->dvert[v_idx_best], + defbase_tot, + defbase_sel, + defbase_tot_sel, + ts->auto_normalize); + + /* If auto-normalize is enabled, but weights are not normalized, + * the value can exceed 1. */ + CLAMP(vgroup_weight, 0.0f, 1.0f); + } + + MEM_freeN(defbase_sel); + } + + BKE_brush_weight_set(vc.scene, brush, vgroup_weight); + changed = true; + } + } + + if (changed) { + /* not really correct since the brush didn't change, but redraws the toolbar */ + WM_main_add_notifier(NC_BRUSH | NA_EDITED, NULL); /* ts->wpaint->paint.brush */ + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_weight_sample(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Weight Paint Sample Weight"; - ot->idname = "PAINT_OT_weight_sample"; - ot->description = "Use the mouse to sample a weight in the 3D view"; + /* identifiers */ + ot->name = "Weight Paint Sample Weight"; + ot->idname = "PAINT_OT_weight_sample"; + ot->description = "Use the mouse to sample a weight in the 3D view"; - /* api callbacks */ - ot->invoke = weight_sample_invoke; - ot->poll = weight_paint_mode_poll; + /* api callbacks */ + ot->invoke = weight_sample_invoke; + ot->poll = weight_paint_mode_poll; - /* flags */ - ot->flag = OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_UNDO; } /** \} */ @@ -270,132 +283,137 @@ void PAINT_OT_weight_sample(wmOperatorType *ot) * \{ */ /* samples cursor location, and gives menu with vertex groups to activate */ -static bool weight_paint_sample_enum_itemf__helper(const MDeformVert *dvert, const int defbase_tot, int *groups) +static bool weight_paint_sample_enum_itemf__helper(const MDeformVert *dvert, + const int defbase_tot, + int *groups) { - /* this func fills in used vgroup's */ - bool found = false; - int i = dvert->totweight; - MDeformWeight *dw; - for (dw = dvert->dw; i > 0; dw++, i--) { - if (dw->def_nr < defbase_tot) { - groups[dw->def_nr] = true; - found = true; - } - } - return found; + /* this func fills in used vgroup's */ + bool found = false; + int i = dvert->totweight; + MDeformWeight *dw; + for (dw = dvert->dw; i > 0; dw++, i--) { + if (dw->def_nr < defbase_tot) { + groups[dw->def_nr] = true; + found = true; + } + } + return found; } -static const EnumPropertyItem *weight_paint_sample_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) { - if (C) { - wmWindow *win = CTX_wm_window(C); - if (win && win->eventstate) { - ViewContext vc; - Mesh *me; - - ED_view3d_viewcontext_init(C, &vc); - me = BKE_mesh_from_object(vc.obact); - - if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { - const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); - const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); - bool found = false; - uint index; - - const int mval[2] = { - win->eventstate->x - vc.ar->winrct.xmin, - win->eventstate->y - vc.ar->winrct.ymin, - }; - - view3d_operator_needs_opengl(C); - ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d); - - if (use_vert_sel) { - if (ED_mesh_pick_vert(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index)) { - MDeformVert *dvert = &me->dvert[index]; - found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups); - } - } - else { - if (ED_mesh_pick_face(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { - const MPoly *mp = &me->mpoly[index]; - uint fidx = mp->totloop - 1; - - do { - MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v]; - found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups); - } while (fidx--); - } - } - - if (found == false) { - MEM_freeN(groups); - } - else { - EnumPropertyItem *item = NULL, item_tmp = {0}; - int totitem = 0; - int i = 0; - bDeformGroup *dg; - for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) { - if (groups[i]) { - item_tmp.identifier = item_tmp.name = dg->name; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - } - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - MEM_freeN(groups); - return item; - } - } - } - } - - return DummyRNA_NULL_items; + if (C) { + wmWindow *win = CTX_wm_window(C); + if (win && win->eventstate) { + ViewContext vc; + Mesh *me; + + ED_view3d_viewcontext_init(C, &vc); + me = BKE_mesh_from_object(vc.obact); + + if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { + const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); + bool found = false; + uint index; + + const int mval[2] = { + win->eventstate->x - vc.ar->winrct.xmin, + win->eventstate->y - vc.ar->winrct.ymin, + }; + + view3d_operator_needs_opengl(C); + ED_view3d_init_mats_rv3d(vc.obact, vc.rv3d); + + if (use_vert_sel) { + if (ED_mesh_pick_vert(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index)) { + MDeformVert *dvert = &me->dvert[index]; + found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups); + } + } + else { + if (ED_mesh_pick_face(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { + const MPoly *mp = &me->mpoly[index]; + uint fidx = mp->totloop - 1; + + do { + MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v]; + found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups); + } while (fidx--); + } + } + + if (found == false) { + MEM_freeN(groups); + } + else { + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + bDeformGroup *dg; + for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) { + if (groups[i]) { + item_tmp.identifier = item_tmp.name = dg->name; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + MEM_freeN(groups); + return item; + } + } + } + } + + return DummyRNA_NULL_items; } static int weight_sample_group_exec(bContext *C, wmOperator *op) { - int type = RNA_enum_get(op->ptr, "group"); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + int type = RNA_enum_get(op->ptr, "group"); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); - BLI_assert(type + 1 >= 0); - vc.obact->actdef = type + 1; + BLI_assert(type + 1 >= 0); + vc.obact->actdef = type + 1; - DEG_id_tag_update(&vc.obact->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact); - return OPERATOR_FINISHED; + DEG_id_tag_update(&vc.obact->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact); + return OPERATOR_FINISHED; } /* TODO, we could make this a menu into OBJECT_OT_vertex_group_set_active * rather than its own operator */ void PAINT_OT_weight_sample_group(wmOperatorType *ot) { - PropertyRNA *prop = NULL; - - /* identifiers */ - ot->name = "Weight Paint Sample Group"; - ot->idname = "PAINT_OT_weight_sample_group"; - ot->description = "Select one of the vertex groups available under current mouse position"; - - /* api callbacks */ - ot->exec = weight_sample_group_exec; - ot->invoke = WM_menu_invoke; - ot->poll = weight_paint_mode_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* keyingset to use (dynamic enum) */ - prop = RNA_def_enum(ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use"); - RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; + PropertyRNA *prop = NULL; + + /* identifiers */ + ot->name = "Weight Paint Sample Group"; + ot->idname = "PAINT_OT_weight_sample_group"; + ot->description = "Select one of the vertex groups available under current mouse position"; + + /* api callbacks */ + ot->exec = weight_sample_group_exec; + ot->invoke = WM_menu_invoke; + ot->poll = weight_paint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* keyingset to use (dynamic enum) */ + prop = RNA_def_enum( + ot->srna, "group", DummyRNA_DEFAULT_items, 0, "Keying Set", "The Keying Set to use"); + RNA_def_enum_funcs(prop, weight_paint_sample_enum_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + ot->prop = prop; } /** \} */ @@ -407,123 +425,122 @@ void PAINT_OT_weight_sample_group(wmOperatorType *ot) /* fills in the selected faces with the current weight and vertex group */ static bool weight_paint_set(Object *ob, float paintweight) { - Mesh *me = ob->data; - const MPoly *mp; - MDeformWeight *dw, *dw_prev; - int vgroup_active, vgroup_mirror = -1; - uint index; - const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - - /* mutually exclusive, could be made into a */ - const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me); - - if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) { - return false; - } - - vgroup_active = ob->actdef - 1; - - /* if mirror painting, find the other group */ - if (me->editflag & ME_EDIT_MIRROR_X) { - vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active); - } - - struct WPaintPrev wpp; - wpaint_prev_create(&wpp, me->dvert, me->totvert); - - for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) { - uint fidx = mp->totloop - 1; - - if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) { - continue; - } - - do { - uint vidx = me->mloop[mp->loopstart + fidx].v; - - if (!me->dvert[vidx].flag) { - if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) { - continue; - } - - dw = defvert_verify_index(&me->dvert[vidx], vgroup_active); - if (dw) { - dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active); - dw_prev->weight = dw->weight; /* set the undo weight */ - dw->weight = paintweight; - - if (me->editflag & ME_EDIT_MIRROR_X) { /* x mirror painting */ - int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology); - if (j >= 0) { - /* copy, not paint again */ - if (vgroup_mirror != -1) { - dw = defvert_verify_index(me->dvert + j, vgroup_mirror); - dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_mirror); - } - else { - dw = defvert_verify_index(me->dvert + j, vgroup_active); - dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active); - } - dw_prev->weight = dw->weight; /* set the undo weight */ - dw->weight = paintweight; - } - } - } - me->dvert[vidx].flag = 1; - } - - } while (fidx--); - } - - { - MDeformVert *dv = me->dvert; - for (index = me->totvert; index != 0; index--, dv++) { - dv->flag = 0; - } - } - - wpaint_prev_destroy(&wpp); - - DEG_id_tag_update(&me->id, 0); - - return true; + Mesh *me = ob->data; + const MPoly *mp; + MDeformWeight *dw, *dw_prev; + int vgroup_active, vgroup_mirror = -1; + uint index; + const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + + /* mutually exclusive, could be made into a */ + const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me); + + if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) { + return false; + } + + vgroup_active = ob->actdef - 1; + + /* if mirror painting, find the other group */ + if (me->editflag & ME_EDIT_MIRROR_X) { + vgroup_mirror = ED_wpaint_mirror_vgroup_ensure(ob, vgroup_active); + } + + struct WPaintPrev wpp; + wpaint_prev_create(&wpp, me->dvert, me->totvert); + + for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) { + uint fidx = mp->totloop - 1; + + if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) { + continue; + } + + do { + uint vidx = me->mloop[mp->loopstart + fidx].v; + + if (!me->dvert[vidx].flag) { + if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) { + continue; + } + + dw = defvert_verify_index(&me->dvert[vidx], vgroup_active); + if (dw) { + dw_prev = defvert_verify_index(wpp.wpaint_prev + vidx, vgroup_active); + dw_prev->weight = dw->weight; /* set the undo weight */ + dw->weight = paintweight; + + if (me->editflag & ME_EDIT_MIRROR_X) { /* x mirror painting */ + int j = mesh_get_x_mirror_vert(ob, NULL, vidx, topology); + if (j >= 0) { + /* copy, not paint again */ + if (vgroup_mirror != -1) { + dw = defvert_verify_index(me->dvert + j, vgroup_mirror); + dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_mirror); + } + else { + dw = defvert_verify_index(me->dvert + j, vgroup_active); + dw_prev = defvert_verify_index(wpp.wpaint_prev + j, vgroup_active); + } + dw_prev->weight = dw->weight; /* set the undo weight */ + dw->weight = paintweight; + } + } + } + me->dvert[vidx].flag = 1; + } + + } while (fidx--); + } + + { + MDeformVert *dv = me->dvert; + for (index = me->totvert; index != 0; index--, dv++) { + dv->flag = 0; + } + } + + wpaint_prev_destroy(&wpp); + + DEG_id_tag_update(&me->id, 0); + + return true; } - static int weight_paint_set_exec(bContext *C, wmOperator *op) { - struct Scene *scene = CTX_data_scene(C); - Object *obact = CTX_data_active_object(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Brush *brush = BKE_paint_brush(&ts->wpaint->paint); - float vgroup_weight = BKE_brush_weight_get(scene, brush); - - if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, NULL) == false) { - return OPERATOR_CANCELLED; - } - - if (weight_paint_set(obact, vgroup_weight)) { - ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */ - return OPERATOR_FINISHED; - } - else { - return OPERATOR_CANCELLED; - } + struct Scene *scene = CTX_data_scene(C); + Object *obact = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_paint_brush(&ts->wpaint->paint); + float vgroup_weight = BKE_brush_weight_get(scene, brush); + + if (ED_wpaint_ensure_data(C, op->reports, WPAINT_ENSURE_MIRROR, NULL) == false) { + return OPERATOR_CANCELLED; + } + + if (weight_paint_set(obact, vgroup_weight)) { + ED_region_tag_redraw(CTX_wm_region(C)); /* XXX - should redraw all 3D views */ + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } void PAINT_OT_weight_set(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Weight"; - ot->idname = "PAINT_OT_weight_set"; - ot->description = "Fill the active vertex group with the current paint weight"; + /* identifiers */ + ot->name = "Set Weight"; + ot->idname = "PAINT_OT_weight_set"; + ot->description = "Fill the active vertex group with the current paint weight"; - /* api callbacks */ - ot->exec = weight_paint_set_exec; - ot->poll = mask_paint_poll; + /* api callbacks */ + ot->exec = weight_paint_set_exec; + ot->poll = mask_paint_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ @@ -534,339 +551,340 @@ void PAINT_OT_weight_set(wmOperatorType *ot) /* *** VGroups Gradient *** */ typedef struct WPGradient_vertStore { - float sco[2]; - float weight_orig; - enum { - VGRAD_STORE_NOP = 0, - VGRAD_STORE_DW_EXIST = (1 << 0), - } flag; + float sco[2]; + float weight_orig; + enum { + VGRAD_STORE_NOP = 0, + VGRAD_STORE_DW_EXIST = (1 << 0), + } flag; } WPGradient_vertStore; typedef struct WPGradient_vertStoreBase { - struct WPaintPrev wpp; - WPGradient_vertStore elem[0]; + struct WPaintPrev wpp; + WPGradient_vertStore elem[0]; } WPGradient_vertStoreBase; typedef struct WPGradient_userData { - struct ARegion *ar; - Scene *scene; - Mesh *me; - Brush *brush; - const float *sco_start; /* [2] */ - const float *sco_end; /* [2] */ - float sco_line_div; /* store (1.0f / len_v2v2(sco_start, sco_end)) */ - int def_nr; - bool is_init; - WPGradient_vertStoreBase *vert_cache; - /* only for init */ - BLI_bitmap *vert_visit; - - /* options */ - short use_select; - short type; - float weightpaint; + struct ARegion *ar; + Scene *scene; + Mesh *me; + Brush *brush; + const float *sco_start; /* [2] */ + const float *sco_end; /* [2] */ + float sco_line_div; /* store (1.0f / len_v2v2(sco_start, sco_end)) */ + int def_nr; + bool is_init; + WPGradient_vertStoreBase *vert_cache; + /* only for init */ + BLI_bitmap *vert_visit; + + /* options */ + short use_select; + short type; + float weightpaint; } WPGradient_userData; static void gradientVert_update(WPGradient_userData *grad_data, int index) { - Mesh *me = grad_data->me; - WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; - float alpha; - - if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) { - alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end); - } - else { - BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL); - alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div; - } - /* no need to clamp 'alpha' yet */ - - /* adjust weight */ - alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f); - - if (alpha != 0.0f) { - MDeformVert *dv = &me->dvert[index]; - MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr); - // dw->weight = alpha; // testing - int tool = grad_data->brush->blend; - float testw; - - /* init if we just added */ - testw = ED_wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha); - CLAMP(testw, 0.0f, 1.0f); - dw->weight = testw; - } - else { - MDeformVert *dv = &me->dvert[index]; - if (vs->flag & VGRAD_STORE_DW_EXIST) { - /* normally we NULL check, but in this case we know it exists */ - MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); - dw->weight = vs->weight_orig; - } - else { - /* wasn't originally existing, remove */ - MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); - if (dw) { - defvert_remove_group(dv, dw); - } - } - } + Mesh *me = grad_data->me; + WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; + float alpha; + + if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) { + alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end); + } + else { + BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL); + alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div; + } + /* no need to clamp 'alpha' yet */ + + /* adjust weight */ + alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f); + + if (alpha != 0.0f) { + MDeformVert *dv = &me->dvert[index]; + MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr); + // dw->weight = alpha; // testing + int tool = grad_data->brush->blend; + float testw; + + /* init if we just added */ + testw = ED_wpaint_blend_tool( + tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha); + CLAMP(testw, 0.0f, 1.0f); + dw->weight = testw; + } + else { + MDeformVert *dv = &me->dvert[index]; + if (vs->flag & VGRAD_STORE_DW_EXIST) { + /* normally we NULL check, but in this case we know it exists */ + MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); + dw->weight = vs->weight_orig; + } + else { + /* wasn't originally existing, remove */ + MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); + if (dw) { + defvert_remove_group(dv, dw); + } + } + } } -static void gradientVertUpdate__mapFunc( - void *userData, int index, const float UNUSED(co[3]), - const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) +static void gradientVertUpdate__mapFunc(void *userData, + int index, + const float UNUSED(co[3]), + const float UNUSED(no_f[3]), + const short UNUSED(no_s[3])) { - WPGradient_userData *grad_data = userData; - WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; + WPGradient_userData *grad_data = userData; + WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; - if (vs->sco[0] == FLT_MAX) { - return; - } + if (vs->sco[0] == FLT_MAX) { + return; + } - gradientVert_update(grad_data, index); + gradientVert_update(grad_data, index); } -static void gradientVertInit__mapFunc( - void *userData, int index, const float co[3], - const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) +static void gradientVertInit__mapFunc(void *userData, + int index, + const float co[3], + const float UNUSED(no_f[3]), + const short UNUSED(no_s[3])) { - WPGradient_userData *grad_data = userData; - Mesh *me = grad_data->me; - WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; - - if (grad_data->use_select && !(me->mvert[index].flag & SELECT)) { - copy_v2_fl(vs->sco, FLT_MAX); - return; - } - - /* run first pass only, - * the screen coords of the verts need to be cached because - * updating the mesh may move them about (entering feedback loop) */ - if (BLI_BITMAP_TEST(grad_data->vert_visit, index)) { - copy_v2_fl(vs->sco, FLT_MAX); - return; - } - - if (ED_view3d_project_float_object( - grad_data->ar, - co, vs->sco, - V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK) - { - return; - } - - MDeformVert *dv = &me->dvert[index]; - const MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); - if (dw) { - vs->weight_orig = dw->weight; - vs->flag = VGRAD_STORE_DW_EXIST; - } - else { - vs->weight_orig = 0.0f; - vs->flag = VGRAD_STORE_NOP; - } - BLI_BITMAP_ENABLE(grad_data->vert_visit, index); - gradientVert_update(grad_data, index); + WPGradient_userData *grad_data = userData; + Mesh *me = grad_data->me; + WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index]; + + if (grad_data->use_select && !(me->mvert[index].flag & SELECT)) { + copy_v2_fl(vs->sco, FLT_MAX); + return; + } + + /* run first pass only, + * the screen coords of the verts need to be cached because + * updating the mesh may move them about (entering feedback loop) */ + if (BLI_BITMAP_TEST(grad_data->vert_visit, index)) { + copy_v2_fl(vs->sco, FLT_MAX); + return; + } + + if (ED_view3d_project_float_object( + grad_data->ar, co, vs->sco, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) != + V3D_PROJ_RET_OK) { + return; + } + + MDeformVert *dv = &me->dvert[index]; + const MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); + if (dw) { + vs->weight_orig = dw->weight; + vs->flag = VGRAD_STORE_DW_EXIST; + } + else { + vs->weight_orig = 0.0f; + vs->flag = VGRAD_STORE_NOP; + } + BLI_BITMAP_ENABLE(grad_data->vert_visit, index); + gradientVert_update(grad_data, index); } static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEvent *event) { - wmGesture *gesture = op->customdata; - WPGradient_vertStoreBase *vert_cache = gesture->userdata; - int ret = WM_gesture_straightline_modal(C, op, event); - - if (ret & OPERATOR_RUNNING_MODAL) { - if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { /* XXX, hardcoded */ - /* generally crap! redo! */ - WM_gesture_straightline_cancel(C, op); - ret &= ~OPERATOR_RUNNING_MODAL; - ret |= OPERATOR_FINISHED; - } - } - - if (ret & OPERATOR_CANCELLED) { - Object *ob = CTX_data_active_object(C); - if (vert_cache != NULL) { - Mesh *me = ob->data; - if (vert_cache->wpp.wpaint_prev) { - BKE_defvert_array_free_elems(me->dvert, me->totvert); - BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert); - wpaint_prev_destroy(&vert_cache->wpp); - } - MEM_freeN(vert_cache); - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - } - else if (ret & OPERATOR_FINISHED) { - wpaint_prev_destroy(&vert_cache->wpp); - MEM_freeN(vert_cache); - } - - return ret; + wmGesture *gesture = op->customdata; + WPGradient_vertStoreBase *vert_cache = gesture->userdata; + int ret = WM_gesture_straightline_modal(C, op, event); + + if (ret & OPERATOR_RUNNING_MODAL) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { /* XXX, hardcoded */ + /* generally crap! redo! */ + WM_gesture_straightline_cancel(C, op); + ret &= ~OPERATOR_RUNNING_MODAL; + ret |= OPERATOR_FINISHED; + } + } + + if (ret & OPERATOR_CANCELLED) { + Object *ob = CTX_data_active_object(C); + if (vert_cache != NULL) { + Mesh *me = ob->data; + if (vert_cache->wpp.wpaint_prev) { + BKE_defvert_array_free_elems(me->dvert, me->totvert); + BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert); + wpaint_prev_destroy(&vert_cache->wpp); + } + MEM_freeN(vert_cache); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } + else if (ret & OPERATOR_FINISHED) { + wpaint_prev_destroy(&vert_cache->wpp); + MEM_freeN(vert_cache); + } + + return ret; } static int paint_weight_gradient_exec(bContext *C, wmOperator *op) { - wmGesture *gesture = op->customdata; - WPGradient_vertStoreBase *vert_cache; - struct ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - Mesh *me = ob->data; - int x_start = RNA_int_get(op->ptr, "xstart"); - int y_start = RNA_int_get(op->ptr, "ystart"); - int x_end = RNA_int_get(op->ptr, "xend"); - int y_end = RNA_int_get(op->ptr, "yend"); - float sco_start[2] = {x_start, y_start}; - float sco_end[2] = {x_end, y_end}; - const bool is_interactive = (gesture != NULL); - - Depsgraph *depsgraph = CTX_data_depsgraph(C); - - WPGradient_userData data = {NULL}; - - if (is_interactive) { - if (gesture->userdata == NULL) { - gesture->userdata = MEM_mallocN( - sizeof(WPGradient_vertStoreBase) + - (sizeof(WPGradient_vertStore) * me->totvert), - __func__); - gesture->userdata_free = false; - data.is_init = true; - - wpaint_prev_create(&((WPGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert); - - /* on init only, convert face -> vert sel */ - if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { - BKE_mesh_flush_select_from_polys(me); - } - } - - vert_cache = gesture->userdata; - } - else { - if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) { - return OPERATOR_CANCELLED; - } - - data.is_init = true; - vert_cache = MEM_mallocN( - sizeof(WPGradient_vertStoreBase) + - (sizeof(WPGradient_vertStore) * me->totvert), - __func__); - } - - data.ar = ar; - data.scene = scene; - data.me = ob->data; - data.sco_start = sco_start; - data.sco_end = sco_end; - data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end); - data.def_nr = ob->actdef - 1; - data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)); - data.vert_cache = vert_cache; - data.vert_visit = NULL; - data.type = RNA_enum_get(op->ptr, "type"); - - { - ToolSettings *ts = CTX_data_tool_settings(C); - VPaint *wp = ts->wpaint; - struct Brush *brush = BKE_paint_brush(&wp->paint); - - curvemapping_initialize(brush->curve); - - data.brush = brush; - data.weightpaint = BKE_brush_weight_get(scene, brush); - } - - ED_view3d_init_mats_rv3d(ob, ar->regiondata); - - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - - CustomData_MeshMasks cddata_masks = scene->customdata_mask; - cddata_masks.vmask |= CD_MASK_ORIGINDEX; - cddata_masks.emask |= CD_MASK_ORIGINDEX; - cddata_masks.pmask |= CD_MASK_ORIGINDEX; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); - if (data.is_init) { - data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__); - - BKE_mesh_foreach_mapped_vert(me_eval, gradientVertInit__mapFunc, &data, MESH_FOREACH_NOP); - - MEM_freeN(data.vert_visit); - data.vert_visit = NULL; - } - else { - BKE_mesh_foreach_mapped_vert(me_eval, gradientVertUpdate__mapFunc, &data, MESH_FOREACH_NOP); - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - - if (is_interactive == false) { - MEM_freeN(vert_cache); - } - - return OPERATOR_FINISHED; + wmGesture *gesture = op->customdata; + WPGradient_vertStoreBase *vert_cache; + struct ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Mesh *me = ob->data; + int x_start = RNA_int_get(op->ptr, "xstart"); + int y_start = RNA_int_get(op->ptr, "ystart"); + int x_end = RNA_int_get(op->ptr, "xend"); + int y_end = RNA_int_get(op->ptr, "yend"); + float sco_start[2] = {x_start, y_start}; + float sco_end[2] = {x_end, y_end}; + const bool is_interactive = (gesture != NULL); + + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + WPGradient_userData data = {NULL}; + + if (is_interactive) { + if (gesture->userdata == NULL) { + gesture->userdata = MEM_mallocN(sizeof(WPGradient_vertStoreBase) + + (sizeof(WPGradient_vertStore) * me->totvert), + __func__); + gesture->userdata_free = false; + data.is_init = true; + + wpaint_prev_create( + &((WPGradient_vertStoreBase *)gesture->userdata)->wpp, me->dvert, me->totvert); + + /* on init only, convert face -> vert sel */ + if (me->editflag & ME_EDIT_PAINT_FACE_SEL) { + BKE_mesh_flush_select_from_polys(me); + } + } + + vert_cache = gesture->userdata; + } + else { + if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) { + return OPERATOR_CANCELLED; + } + + data.is_init = true; + vert_cache = MEM_mallocN( + sizeof(WPGradient_vertStoreBase) + (sizeof(WPGradient_vertStore) * me->totvert), __func__); + } + + data.ar = ar; + data.scene = scene; + data.me = ob->data; + data.sco_start = sco_start; + data.sco_end = sco_end; + data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end); + data.def_nr = ob->actdef - 1; + data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)); + data.vert_cache = vert_cache; + data.vert_visit = NULL; + data.type = RNA_enum_get(op->ptr, "type"); + + { + ToolSettings *ts = CTX_data_tool_settings(C); + VPaint *wp = ts->wpaint; + struct Brush *brush = BKE_paint_brush(&wp->paint); + + curvemapping_initialize(brush->curve); + + data.brush = brush; + data.weightpaint = BKE_brush_weight_get(scene, brush); + } + + ED_view3d_init_mats_rv3d(ob, ar->regiondata); + + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + CustomData_MeshMasks cddata_masks = scene->customdata_mask; + cddata_masks.vmask |= CD_MASK_ORIGINDEX; + cddata_masks.emask |= CD_MASK_ORIGINDEX; + cddata_masks.pmask |= CD_MASK_ORIGINDEX; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &cddata_masks); + if (data.is_init) { + data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__); + + BKE_mesh_foreach_mapped_vert(me_eval, gradientVertInit__mapFunc, &data, MESH_FOREACH_NOP); + + MEM_freeN(data.vert_visit); + data.vert_visit = NULL; + } + else { + BKE_mesh_foreach_mapped_vert(me_eval, gradientVertUpdate__mapFunc, &data, MESH_FOREACH_NOP); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + if (is_interactive == false) { + MEM_freeN(vert_cache); + } + + return OPERATOR_FINISHED; } static int paint_weight_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - int ret; - - if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) { - return OPERATOR_CANCELLED; - } - - ret = WM_gesture_straightline_invoke(C, op, event); - if (ret & OPERATOR_RUNNING_MODAL) { - struct ARegion *ar = CTX_wm_region(C); - if (ar->regiontype == RGN_TYPE_WINDOW) { - /* TODO, hardcoded, extend WM_gesture_straightline_ */ - if (event->type == LEFTMOUSE && event->val == KM_PRESS) { - wmGesture *gesture = op->customdata; - gesture->is_active = true; - } - } - } - return ret; + int ret; + + if (ED_wpaint_ensure_data(C, op->reports, 0, NULL) == false) { + return OPERATOR_CANCELLED; + } + + ret = WM_gesture_straightline_invoke(C, op, event); + if (ret & OPERATOR_RUNNING_MODAL) { + struct ARegion *ar = CTX_wm_region(C); + if (ar->regiontype == RGN_TYPE_WINDOW) { + /* TODO, hardcoded, extend WM_gesture_straightline_ */ + if (event->type == LEFTMOUSE && event->val == KM_PRESS) { + wmGesture *gesture = op->customdata; + gesture->is_active = true; + } + } + } + return ret; } void PAINT_OT_weight_gradient(wmOperatorType *ot) { - /* defined in DNA_space_types.h */ - static const EnumPropertyItem gradient_types[] = { - {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""}, - {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Weight Gradient"; - ot->idname = "PAINT_OT_weight_gradient"; - ot->description = "Draw a line to apply a weight gradient to selected vertices"; - - /* api callbacks */ - ot->invoke = paint_weight_gradient_invoke; - ot->modal = paint_weight_gradient_modal; - ot->exec = paint_weight_gradient_exec; - ot->poll = weight_paint_poll_ignore_tool; - ot->cancel = WM_gesture_straightline_cancel; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", ""); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT); + /* defined in DNA_space_types.h */ + static const EnumPropertyItem gradient_types[] = { + {WPAINT_GRADIENT_TYPE_LINEAR, "LINEAR", 0, "Linear", ""}, + {WPAINT_GRADIENT_TYPE_RADIAL, "RADIAL", 0, "Radial", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Weight Gradient"; + ot->idname = "PAINT_OT_weight_gradient"; + ot->description = "Draw a line to apply a weight gradient to selected vertices"; + + /* api callbacks */ + ot->invoke = paint_weight_gradient_invoke; + ot->modal = paint_weight_gradient_modal; + ot->exec = paint_weight_gradient_exec; + ot->poll = weight_paint_poll_ignore_tool; + ot->cancel = WM_gesture_straightline_cancel; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_enum(ot->srna, "type", gradient_types, 0, "Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c index 38f9f1dfa9f..88aed201b48 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -47,111 +47,112 @@ #include "WM_api.h" #include "WM_types.h" -#include "paint_intern.h" /* own include */ +#include "paint_intern.h" /* own include */ /* -------------------------------------------------------------------- */ /** \name Weight Paint Sanity Checks * \{ */ /* ensure we have data on wpaint start, add if needed */ -bool ED_wpaint_ensure_data( - bContext *C, struct ReportList *reports, - enum eWPaintFlag flag, struct WPaintVGroupIndex *vgroup_index) +bool ED_wpaint_ensure_data(bContext *C, + struct ReportList *reports, + enum eWPaintFlag flag, + struct WPaintVGroupIndex *vgroup_index) { - Object *ob = CTX_data_active_object(C); - Mesh *me = BKE_mesh_from_object(ob); + Object *ob = CTX_data_active_object(C); + Mesh *me = BKE_mesh_from_object(ob); - if (vgroup_index) { - vgroup_index->active = -1; - vgroup_index->mirror = -1; - } + if (vgroup_index) { + vgroup_index->active = -1; + vgroup_index->mirror = -1; + } - if (BKE_object_is_in_editmode(ob)) { - return false; - } + if (BKE_object_is_in_editmode(ob)) { + return false; + } - if (me == NULL || me->totpoly == 0) { - return false; - } + if (me == NULL || me->totpoly == 0) { + return false; + } - /* if nothing was added yet, we make dverts and a vertex deform group */ - if (!me->dvert) { - BKE_object_defgroup_data_create(&me->id); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); - } + /* if nothing was added yet, we make dverts and a vertex deform group */ + if (!me->dvert) { + BKE_object_defgroup_data_create(&me->id); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); + } - /* this happens on a Bone select, when no vgroup existed yet */ - if (ob->actdef <= 0) { - Object *modob; - if ((modob = modifiers_isDeformedByArmature(ob))) { - Bone *actbone = ((bArmature *)modob->data)->act_bone; - if (actbone) { - bPoseChannel *pchan = BKE_pose_channel_find_name(modob->pose, actbone->name); + /* this happens on a Bone select, when no vgroup existed yet */ + if (ob->actdef <= 0) { + Object *modob; + if ((modob = modifiers_isDeformedByArmature(ob))) { + Bone *actbone = ((bArmature *)modob->data)->act_bone; + if (actbone) { + bPoseChannel *pchan = BKE_pose_channel_find_name(modob->pose, actbone->name); - if (pchan) { - bDeformGroup *dg = defgroup_find_name(ob, pchan->name); - if (dg == NULL) { - dg = BKE_object_defgroup_add_name(ob, pchan->name); /* sets actdef */ - } - else { - int actdef = 1 + BLI_findindex(&ob->defbase, dg); - BLI_assert(actdef >= 0); - ob->actdef = actdef; - } - } - } - } - } - if (BLI_listbase_is_empty(&ob->defbase)) { - BKE_object_defgroup_add(ob); - } + if (pchan) { + bDeformGroup *dg = defgroup_find_name(ob, pchan->name); + if (dg == NULL) { + dg = BKE_object_defgroup_add_name(ob, pchan->name); /* sets actdef */ + } + else { + int actdef = 1 + BLI_findindex(&ob->defbase, dg); + BLI_assert(actdef >= 0); + ob->actdef = actdef; + } + } + } + } + } + if (BLI_listbase_is_empty(&ob->defbase)) { + BKE_object_defgroup_add(ob); + } - /* ensure we don't try paint onto an invalid group */ - if (ob->actdef <= 0) { - BKE_report(reports, RPT_WARNING, "No active vertex group for painting, aborting"); - return false; - } + /* ensure we don't try paint onto an invalid group */ + if (ob->actdef <= 0) { + BKE_report(reports, RPT_WARNING, "No active vertex group for painting, aborting"); + return false; + } - if (vgroup_index) { - vgroup_index->active = ob->actdef - 1; - } + if (vgroup_index) { + vgroup_index->active = ob->actdef - 1; + } - if (flag & WPAINT_ENSURE_MIRROR) { - if (me->editflag & ME_EDIT_MIRROR_X) { - int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1); - if (vgroup_index) { - vgroup_index->mirror = mirror; - } - } - } + if (flag & WPAINT_ENSURE_MIRROR) { + if (me->editflag & ME_EDIT_MIRROR_X) { + int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1); + if (vgroup_index) { + vgroup_index->mirror = mirror; + } + } + } - return true; + return true; } /** \} */ /* mirror_vgroup is set to -1 when invalid */ int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) { - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, vgroup_active); + bDeformGroup *defgroup = BLI_findlink(&ob->defbase, vgroup_active); - if (defgroup) { - int mirrdef; - char name_flip[MAXBONENAME]; + if (defgroup) { + int mirrdef; + char name_flip[MAXBONENAME]; - BLI_string_flip_side_name(name_flip, defgroup->name, false, sizeof(name_flip)); - mirrdef = defgroup_name_index(ob, name_flip); - if (mirrdef == -1) { - if (BKE_defgroup_new(ob, name_flip)) { - mirrdef = BLI_listbase_count(&ob->defbase) - 1; - } - } + BLI_string_flip_side_name(name_flip, defgroup->name, false, sizeof(name_flip)); + mirrdef = defgroup_name_index(ob, name_flip); + if (mirrdef == -1) { + if (BKE_defgroup_new(ob, name_flip)) { + mirrdef = BLI_listbase_count(&ob->defbase) - 1; + } + } - /* curdef should never be NULL unless this is - * a light and BKE_object_defgroup_add_name fails */ - return mirrdef; - } + /* curdef should never be NULL unless this is + * a light and BKE_object_defgroup_add_name fails */ + return mirrdef; + } - return -1; + return -1; } /* -------------------------------------------------------------------- */ @@ -160,144 +161,158 @@ int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) BLI_INLINE float wval_blend(const float weight, const float paintval, const float alpha) { - const float talpha = min_ff(alpha, 1.0f); /* blending with values over 1 doesn't make sense */ - return (paintval * talpha) + (weight * (1.0f - talpha)); + const float talpha = min_ff(alpha, 1.0f); /* blending with values over 1 doesn't make sense */ + return (paintval * talpha) + (weight * (1.0f - talpha)); } BLI_INLINE float wval_add(const float weight, const float paintval, const float alpha) { - return weight + (paintval * alpha); + return weight + (paintval * alpha); } BLI_INLINE float wval_sub(const float weight, const float paintval, const float alpha) { - return weight - (paintval * alpha); + return weight - (paintval * alpha); } BLI_INLINE float wval_mul(const float weight, const float paintval, const float alpha) -{ /* first mul, then blend the fac */ - return ((1.0f - alpha) + (alpha * paintval)) * weight; +{ /* first mul, then blend the fac */ + return ((1.0f - alpha) + (alpha * paintval)) * weight; } BLI_INLINE float wval_lighten(const float weight, const float paintval, const float alpha) { - return (weight < paintval) ? wval_blend(weight, paintval, alpha) : weight; + return (weight < paintval) ? wval_blend(weight, paintval, alpha) : weight; } BLI_INLINE float wval_darken(const float weight, const float paintval, const float alpha) { - return (weight > paintval) ? wval_blend(weight, paintval, alpha) : weight; + return (weight > paintval) ? wval_blend(weight, paintval, alpha) : weight; } /* mainly for color */ BLI_INLINE float wval_colordodge(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - temp = (paintval == 1.0f) ? 1.0f : min_ff((weight * (225.0f / 255.0f)) / (1.0f - paintval), 1.0f); - return mfac * weight + temp * fac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + temp = (paintval == 1.0f) ? 1.0f : + min_ff((weight * (225.0f / 255.0f)) / (1.0f - paintval), 1.0f); + return mfac * weight + temp * fac; } BLI_INLINE float wval_difference(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - temp = fabsf(weight - paintval); - return mfac * weight + temp * fac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + temp = fabsf(weight - paintval); + return mfac * weight + temp * fac; } BLI_INLINE float wval_screen(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - temp = max_ff(1.0f - (((1.0f - weight) * (1.0f - paintval))), 0); - return mfac * weight + temp * fac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + temp = max_ff(1.0f - (((1.0f - weight) * (1.0f - paintval))), 0); + return mfac * weight + temp * fac; } BLI_INLINE float wval_hardlight(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - if (paintval > 0.5f) { - temp = 1.0f - ((1.0f - 2.0f * (paintval - 0.5f)) * (1.0f - weight)); - } - else { - temp = (2.0f * paintval * weight); - } - return mfac * weight + temp * fac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + if (paintval > 0.5f) { + temp = 1.0f - ((1.0f - 2.0f * (paintval - 0.5f)) * (1.0f - weight)); + } + else { + temp = (2.0f * paintval * weight); + } + return mfac * weight + temp * fac; } BLI_INLINE float wval_overlay(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - if (weight > 0.5f) { - temp = 1.0f - ((1.0f - 2.0f * (weight - 0.5f)) * (1.0f - paintval)); - } - else { - temp = (2.0f * paintval * weight); - } - return mfac * weight + temp * fac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + if (weight > 0.5f) { + temp = 1.0f - ((1.0f - 2.0f * (weight - 0.5f)) * (1.0f - paintval)); + } + else { + temp = (2.0f * paintval * weight); + } + return mfac * weight + temp * fac; } BLI_INLINE float wval_softlight(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - if (weight < 0.5f) { - temp = ((2.0f * ((paintval / 2.0f) + 0.25f)) * weight); - } - else { - temp = 1.0f - (2.0f * (1.0f - ((paintval / 2.0f) + 0.25f)) * (1.0f - weight)); - } - return temp * fac + weight * mfac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + if (weight < 0.5f) { + temp = ((2.0f * ((paintval / 2.0f) + 0.25f)) * weight); + } + else { + temp = 1.0f - (2.0f * (1.0f - ((paintval / 2.0f) + 0.25f)) * (1.0f - weight)); + } + return temp * fac + weight * mfac; } BLI_INLINE float wval_exclusion(float weight, float paintval, float fac) { - float mfac, temp; - if (fac == 0.0f) { - return weight; - } - mfac = 1.0f - fac; - temp = 0.5f - ((2.0f * (weight - 0.5f) * (paintval - 0.5f))); - return temp * fac + weight * mfac; + float mfac, temp; + if (fac == 0.0f) { + return weight; + } + mfac = 1.0f - fac; + temp = 0.5f - ((2.0f * (weight - 0.5f) * (paintval - 0.5f))); + return temp * fac + weight * mfac; } /* vpaint has 'vpaint_blend_tool' */ /* result is not clamped from [0-1] */ -float ED_wpaint_blend_tool( - const int tool, - /* dw->weight */ - const float weight, - const float paintval, const float alpha) +float ED_wpaint_blend_tool(const int tool, + /* dw->weight */ + const float weight, + const float paintval, + const float alpha) { - switch ((IMB_BlendMode)tool) { - case IMB_BLEND_MIX: return wval_blend(weight, paintval, alpha); - case IMB_BLEND_ADD: return wval_add(weight, paintval, alpha); - case IMB_BLEND_SUB: return wval_sub(weight, paintval, alpha); - case IMB_BLEND_MUL: return wval_mul(weight, paintval, alpha); - case IMB_BLEND_LIGHTEN: return wval_lighten(weight, paintval, alpha); - case IMB_BLEND_DARKEN: return wval_darken(weight, paintval, alpha); - /* Mostly make sense for color: support anyway. */ - case IMB_BLEND_COLORDODGE: return wval_colordodge(weight, paintval, alpha); - case IMB_BLEND_DIFFERENCE: return wval_difference(weight, paintval, alpha); - case IMB_BLEND_SCREEN: return wval_screen(weight, paintval, alpha); - case IMB_BLEND_HARDLIGHT: return wval_hardlight(weight, paintval, alpha); - case IMB_BLEND_OVERLAY: return wval_overlay(weight, paintval, alpha); - case IMB_BLEND_SOFTLIGHT: return wval_softlight(weight, paintval, alpha); - case IMB_BLEND_EXCLUSION: return wval_exclusion(weight, paintval, alpha); - /* Only for color: just use blend. */ - default: - return wval_blend(weight, paintval, alpha); - } + switch ((IMB_BlendMode)tool) { + case IMB_BLEND_MIX: + return wval_blend(weight, paintval, alpha); + case IMB_BLEND_ADD: + return wval_add(weight, paintval, alpha); + case IMB_BLEND_SUB: + return wval_sub(weight, paintval, alpha); + case IMB_BLEND_MUL: + return wval_mul(weight, paintval, alpha); + case IMB_BLEND_LIGHTEN: + return wval_lighten(weight, paintval, alpha); + case IMB_BLEND_DARKEN: + return wval_darken(weight, paintval, alpha); + /* Mostly make sense for color: support anyway. */ + case IMB_BLEND_COLORDODGE: + return wval_colordodge(weight, paintval, alpha); + case IMB_BLEND_DIFFERENCE: + return wval_difference(weight, paintval, alpha); + case IMB_BLEND_SCREEN: + return wval_screen(weight, paintval, alpha); + case IMB_BLEND_HARDLIGHT: + return wval_hardlight(weight, paintval, alpha); + case IMB_BLEND_OVERLAY: + return wval_overlay(weight, paintval, alpha); + case IMB_BLEND_SOFTLIGHT: + return wval_softlight(weight, paintval, alpha); + case IMB_BLEND_EXCLUSION: + return wval_exclusion(weight, paintval, alpha); + /* Only for color: just use blend. */ + default: + return wval_blend(weight, paintval, alpha); + } } /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index ad2cfb5f9fc..9309def4a77 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -22,7 +22,6 @@ * \ingroup edsculpt */ - #include "MEM_guardedalloc.h" #include "BLI_math.h" @@ -103,188 +102,175 @@ * (used for flushing updates at enter/exit sculpt mode) */ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) { - ModifierData *md; - VirtualModifierData virtualModifierData; + ModifierData *md; + VirtualModifierData virtualModifierData; - md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + md = modifiers_getVirtualModifierList(ob, &virtualModifierData); - /* exception for shape keys because we can edit those */ - for (; md; md = md->next) { - if (modifier_isEnabled(scene, md, eModifierMode_Realtime)) - return 1; - } + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + if (modifier_isEnabled(scene, md, eModifierMode_Realtime)) + return 1; + } - return 0; + return 0; } static bool sculpt_tool_needs_original(const char sculpt_tool) { - return ELEM(sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER); + return ELEM( + sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER); } static bool sculpt_tool_is_proxy_used(const char sculpt_tool) { - return ELEM(sculpt_tool, - SCULPT_TOOL_SMOOTH, - SCULPT_TOOL_LAYER); + return ELEM(sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER); } -static bool sculpt_brush_use_topology_rake( - const SculptSession *ss, const Brush *brush) +static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush) { - return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) && - (brush->topology_rake_factor > 0.0f) && - (ss->bm != NULL); + return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) && + (brush->topology_rake_factor > 0.0f) && (ss->bm != NULL); } /** * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action */ -static int sculpt_brush_needs_normal( - const SculptSession *ss, const Brush *brush) +static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush) { - return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && - (ss->cache->normal_weight > 0.0f)) || + return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && + (ss->cache->normal_weight > 0.0f)) || - ELEM(brush->sculpt_tool, - SCULPT_TOOL_BLOB, - SCULPT_TOOL_CREASE, - SCULPT_TOOL_DRAW, - SCULPT_TOOL_LAYER, - SCULPT_TOOL_NUDGE, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB) || + ELEM(brush->sculpt_tool, + SCULPT_TOOL_BLOB, + SCULPT_TOOL_CREASE, + SCULPT_TOOL_DRAW, + SCULPT_TOOL_LAYER, + SCULPT_TOOL_NUDGE, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB) || - (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) || - sculpt_brush_use_topology_rake(ss, brush); + (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) || + sculpt_brush_use_topology_rake(ss, brush); } /** \} */ static bool sculpt_brush_needs_rake_rotation(const Brush *brush) { - return SCULPT_TOOL_HAS_RAKE(brush->sculpt_tool) && (brush->rake_factor != 0.0f); + return SCULPT_TOOL_HAS_RAKE(brush->sculpt_tool) && (brush->rake_factor != 0.0f); } typedef enum StrokeFlags { - CLIP_X = 1, - CLIP_Y = 2, - CLIP_Z = 4, + CLIP_X = 1, + CLIP_Y = 2, + CLIP_Z = 4, } StrokeFlags; /************** Access to original unmodified vertex data *************/ typedef struct { - BMLog *bm_log; + BMLog *bm_log; - SculptUndoNode *unode; - float (*coords)[3]; - short (*normals)[3]; - const float *vmasks; + SculptUndoNode *unode; + float (*coords)[3]; + short (*normals)[3]; + const float *vmasks; - /* Original coordinate, normal, and mask */ - const float *co; - const short *no; - float mask; + /* Original coordinate, normal, and mask */ + const float *co; + const short *no; + float mask; } SculptOrigVertData; - /* Initialize a SculptOrigVertData for accessing original vertex data; * handles BMesh, mesh, and multires */ static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode) { - SculptSession *ss = ob->sculpt; - BMesh *bm = ss->bm; + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; - memset(data, 0, sizeof(*data)); - data->unode = unode; + memset(data, 0, sizeof(*data)); + data->unode = unode; - if (bm) { - data->bm_log = ss->bm_log; - } - else { - data->coords = data->unode->co; - data->normals = data->unode->no; - data->vmasks = data->unode->mask; - } + if (bm) { + data->bm_log = ss->bm_log; + } + else { + data->coords = data->unode->co; + data->normals = data->unode->no; + data->vmasks = data->unode->mask; + } } /* Initialize a SculptOrigVertData for accessing original vertex data; * handles BMesh, mesh, and multires */ -static void sculpt_orig_vert_data_init(SculptOrigVertData *data, - Object *ob, - PBVHNode *node) +static void sculpt_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node) { - SculptUndoNode *unode; - unode = sculpt_undo_push_node(ob, node, SCULPT_UNDO_COORDS); - sculpt_orig_vert_data_unode_init(data, ob, unode); + SculptUndoNode *unode; + unode = sculpt_undo_push_node(ob, node, SCULPT_UNDO_COORDS); + sculpt_orig_vert_data_unode_init(data, ob, unode); } /* Update a SculptOrigVertData for a particular vertex from the PBVH * iterator */ -static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, - PBVHVertexIter *iter) -{ - if (orig_data->unode->type == SCULPT_UNDO_COORDS) { - if (orig_data->bm_log) { - BM_log_original_vert_data( - orig_data->bm_log, iter->bm_vert, - &orig_data->co, &orig_data->no); - } - else { - orig_data->co = orig_data->coords[iter->i]; - orig_data->no = orig_data->normals[iter->i]; - } - } - else if (orig_data->unode->type == SCULPT_UNDO_MASK) { - if (orig_data->bm_log) { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); - } - else { - orig_data->mask = orig_data->vmasks[iter->i]; - } - } +static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) +{ + if (orig_data->unode->type == SCULPT_UNDO_COORDS) { + if (orig_data->bm_log) { + BM_log_original_vert_data(orig_data->bm_log, iter->bm_vert, &orig_data->co, &orig_data->no); + } + else { + orig_data->co = orig_data->coords[iter->i]; + orig_data->no = orig_data->normals[iter->i]; + } + } + else if (orig_data->unode->type == SCULPT_UNDO_MASK) { + if (orig_data->bm_log) { + orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); + } + else { + orig_data->mask = orig_data->vmasks[iter->i]; + } + } } static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[3]) { - float rake_dist = len_v3v3(srd->follow_co, co); - if (rake_dist > srd->follow_dist) { - interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); - } + float rake_dist = len_v3v3(srd->follow_co, co); + if (rake_dist > srd->follow_dist) { + interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); + } } - -static void sculpt_rake_rotate( - const SculptSession *ss, const float sculpt_co[3], const float v_co[3], float factor, float r_delta[3]) +static void sculpt_rake_rotate(const SculptSession *ss, + const float sculpt_co[3], + const float v_co[3], + float factor, + float r_delta[3]) { - float vec_rot[3]; + float vec_rot[3]; #if 0 - /* lerp */ - sub_v3_v3v3(vec_rot, v_co, sculpt_co); - mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); - add_v3_v3(vec_rot, sculpt_co); - sub_v3_v3v3(r_delta, vec_rot, v_co); - mul_v3_fl(r_delta, factor); + /* lerp */ + sub_v3_v3v3(vec_rot, v_co, sculpt_co); + mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); + mul_v3_fl(r_delta, factor); #else - /* slerp */ - float q_interp[4]; - sub_v3_v3v3(vec_rot, v_co, sculpt_co); + /* slerp */ + float q_interp[4]; + sub_v3_v3v3(vec_rot, v_co, sculpt_co); - copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); - pow_qt_fl_normalized(q_interp, factor); - mul_qt_v3(q_interp, vec_rot); + copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); + pow_qt_fl_normalized(q_interp, factor); + mul_qt_v3(q_interp, vec_rot); - add_v3_v3(vec_rot, sculpt_co); - sub_v3_v3v3(r_delta, vec_rot, v_co); + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); #endif - } /** @@ -292,26 +278,29 @@ static void sculpt_rake_rotate( * * \param grab_delta: Typically from `ss->cache->grab_delta_symmetry`. */ -static void sculpt_project_v3_normal_align(SculptSession *ss, const float normal_weight, float grab_delta[3]) +static void sculpt_project_v3_normal_align(SculptSession *ss, + const float normal_weight, + float grab_delta[3]) { - /* signed to support grabbing in (to make a hole) as well as out. */ - const float len_signed = dot_v3v3(ss->cache->sculpt_normal_symm, grab_delta); + /* signed to support grabbing in (to make a hole) as well as out. */ + const float len_signed = dot_v3v3(ss->cache->sculpt_normal_symm, grab_delta); - /* this scale effectively projects the offset so dragging follows the cursor, - * as the normal points towards the view, the scale increases. */ - float len_view_scale; - { - float view_aligned_normal[3]; - project_plane_v3_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm, ss->cache->view_normal); - len_view_scale = fabsf(dot_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm)); - len_view_scale = (len_view_scale > FLT_EPSILON) ? 1.0f / len_view_scale : 1.0f; - } + /* this scale effectively projects the offset so dragging follows the cursor, + * as the normal points towards the view, the scale increases. */ + float len_view_scale; + { + float view_aligned_normal[3]; + project_plane_v3_v3v3( + view_aligned_normal, ss->cache->sculpt_normal_symm, ss->cache->view_normal); + len_view_scale = fabsf(dot_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm)); + len_view_scale = (len_view_scale > FLT_EPSILON) ? 1.0f / len_view_scale : 1.0f; + } - mul_v3_fl(grab_delta, 1.0f - normal_weight); - madd_v3_v3fl(grab_delta, ss->cache->sculpt_normal_symm, (len_signed * normal_weight) * len_view_scale); + mul_v3_fl(grab_delta, 1.0f - normal_weight); + madd_v3_v3fl( + grab_delta, ss->cache->sculpt_normal_symm, (len_signed * normal_weight) * len_view_scale); } - /** \name SculptProjectVector * * Fast-path for #project_plane_v3_v3v3 @@ -319,43 +308,39 @@ static void sculpt_project_v3_normal_align(SculptSession *ss, const float normal * \{ */ typedef struct SculptProjectVector { - float plane[3]; - float len_sq; - float len_sq_inv_neg; - bool is_valid; + float plane[3]; + float len_sq; + float len_sq_inv_neg; + bool is_valid; } SculptProjectVector; /** * \param plane: Direction, can be any length. */ -static void sculpt_project_v3_cache_init( - SculptProjectVector *spvc, const float plane[3]) +static void sculpt_project_v3_cache_init(SculptProjectVector *spvc, const float plane[3]) { - copy_v3_v3(spvc->plane, plane); - spvc->len_sq = len_squared_v3(spvc->plane); - spvc->is_valid = (spvc->len_sq > FLT_EPSILON); - spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; + copy_v3_v3(spvc->plane, plane); + spvc->len_sq = len_squared_v3(spvc->plane); + spvc->is_valid = (spvc->len_sq > FLT_EPSILON); + spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; } /** * Calculate the projection. */ -static void sculpt_project_v3( - const SculptProjectVector *spvc, const float vec[3], - float r_vec[3]) +static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3], float r_vec[3]) { #if 0 - project_plane_v3_v3v3(r_vec, vec, spvc->plane); + project_plane_v3_v3v3(r_vec, vec, spvc->plane); #else - /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ - madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); + /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ + madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); #endif } /** \} */ - /**********************************************************************/ /* Returns true if the stroke will use dynamic topology, false @@ -364,360 +349,351 @@ static void sculpt_project_v3( * Factors: some brushes like grab cannot do dynamic topology. * Others, like smooth, are better without. Same goes for alt- * key smoothing. */ -static bool sculpt_stroke_is_dynamic_topology( - const SculptSession *ss, const Brush *brush) +static bool sculpt_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush) { - return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && + return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && - (!ss->cache || (!ss->cache->alt_smooth)) && + (!ss->cache || (!ss->cache->alt_smooth)) && - /* Requires mesh restore, which doesn't work with - * dynamic-topology */ - !(brush->flag & BRUSH_ANCHORED) && - !(brush->flag & BRUSH_DRAG_DOT) && + /* Requires mesh restore, which doesn't work with + * dynamic-topology */ + !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); + SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /*** paint mesh ***/ -static void paint_mesh_restore_co_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void paint_mesh_restore_co_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; - SculptUndoNode *unode; - SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); + SculptUndoNode *unode; + SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); - if (ss->bm) { - unode = sculpt_undo_push_node(data->ob, data->nodes[n], type); - } - else { - unode = sculpt_undo_get_node(data->nodes[n]); - } + if (ss->bm) { + unode = sculpt_undo_push_node(data->ob, data->nodes[n], type); + } + else { + unode = sculpt_undo_get_node(data->nodes[n]); + } - if (unode) { - PBVHVertexIter vd; - SculptOrigVertData orig_data; + if (unode) { + PBVHVertexIter vd; + SculptOrigVertData orig_data; - sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode); + sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (orig_data.unode->type == SCULPT_UNDO_COORDS) { - copy_v3_v3(vd.co, orig_data.co); - if (vd.no) - copy_v3_v3_short(vd.no, orig_data.no); - else - normal_short_to_float_v3(vd.fno, orig_data.no); - } - else if (orig_data.unode->type == SCULPT_UNDO_MASK) { - *vd.mask = orig_data.mask; - } + if (orig_data.unode->type == SCULPT_UNDO_COORDS) { + copy_v3_v3(vd.co, orig_data.co); + if (vd.no) + copy_v3_v3_short(vd.no, orig_data.no); + else + normal_short_to_float_v3(vd.fno, orig_data.no); + } + else if (orig_data.unode->type == SCULPT_UNDO_MASK) { + *vd.mask = orig_data.mask; + } - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_update(data->nodes[n]); - } + BKE_pbvh_node_mark_update(data->nodes[n]); + } } static void paint_mesh_restore_co(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - PBVHNode **nodes; - int totnode; + PBVHNode **nodes; + int totnode; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - /* Disable OpenMP when dynamic-topology is enabled. Otherwise, new entries might be inserted by - * sculpt_undo_push_node() into the GHash used internally by BM_log_original_vert_co() by a different thread. - * See T33787. */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* Disable OpenMP when dynamic-topology is enabled. Otherwise, new entries might be inserted by + * sculpt_undo_push_node() into the GHash used internally by BM_log_original_vert_co() by a different thread. + * See T33787. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - paint_mesh_restore_co_task_cb, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && + totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); - if (nodes) - MEM_freeN(nodes); + if (nodes) + MEM_freeN(nodes); } /*** BVH Tree ***/ static void sculpt_extend_redraw_rect_previous(Object *ob, rcti *rect) { - /* expand redraw rect with redraw rect from previous step to - * prevent partial-redraw issues caused by fast strokes. This is - * needed here (not in sculpt_flush_update) as it was before - * because redraw rectangle should be the same in both of - * optimized PBVH draw function and 3d view redraw (if not -- some - * mesh parts could disappear from screen (sergey) */ - SculptSession *ss = ob->sculpt; + /* expand redraw rect with redraw rect from previous step to + * prevent partial-redraw issues caused by fast strokes. This is + * needed here (not in sculpt_flush_update) as it was before + * because redraw rectangle should be the same in both of + * optimized PBVH draw function and 3d view redraw (if not -- some + * mesh parts could disappear from screen (sergey) */ + SculptSession *ss = ob->sculpt; - if (ss->cache) { - if (!BLI_rcti_is_empty(&ss->cache->previous_r)) - BLI_rcti_union(rect, &ss->cache->previous_r); - } + if (ss->cache) { + if (!BLI_rcti_is_empty(&ss->cache->previous_r)) + BLI_rcti_union(rect, &ss->cache->previous_r); + } } /* Get a screen-space rectangle of the modified area */ -bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, - Object *ob, rcti *rect) +bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, Object *ob, rcti *rect) { - PBVH *pbvh = ob->sculpt->pbvh; - float bb_min[3], bb_max[3]; + PBVH *pbvh = ob->sculpt->pbvh; + float bb_min[3], bb_max[3]; - if (!pbvh) - return 0; + if (!pbvh) + return 0; - BKE_pbvh_redraw_BB(pbvh, bb_min, bb_max); + BKE_pbvh_redraw_BB(pbvh, bb_min, bb_max); - /* convert 3D bounding box to screen space */ - if (!paint_convert_bb_to_rect(rect, - bb_min, - bb_max, - ar, - rv3d, - ob)) - { - return 0; - } + /* convert 3D bounding box to screen space */ + if (!paint_convert_bb_to_rect(rect, bb_min, bb_max, ar, rv3d, ob)) { + return 0; + } - - return 1; + return 1; } void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, Object *ob) { - PBVH *pbvh = ob->sculpt->pbvh; - /* copy here, original will be used below */ - rcti rect = ob->sculpt->cache->current_r; + PBVH *pbvh = ob->sculpt->pbvh; + /* copy here, original will be used below */ + rcti rect = ob->sculpt->cache->current_r; - sculpt_extend_redraw_rect_previous(ob, &rect); + sculpt_extend_redraw_rect_previous(ob, &rect); - paint_calc_redraw_planes(planes, ar, ob, &rect); + paint_calc_redraw_planes(planes, ar, ob, &rect); - /* we will draw this rect, so now we can set it as the previous partial rect. - * Note that we don't update with the union of previous/current (rect), only with - * the current. Thus we avoid the rectangle needlessly growing to include - * all the stroke area */ - ob->sculpt->cache->previous_r = ob->sculpt->cache->current_r; + /* we will draw this rect, so now we can set it as the previous partial rect. + * Note that we don't update with the union of previous/current (rect), only with + * the current. Thus we avoid the rectangle needlessly growing to include + * all the stroke area */ + ob->sculpt->cache->previous_r = ob->sculpt->cache->current_r; - /* clear redraw flag from nodes */ - if (pbvh) - BKE_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); + /* clear redraw flag from nodes */ + if (pbvh) + BKE_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); } /************************ Brush Testing *******************/ void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) { - RegionView3D *rv3d = ss->cache->vc->rv3d; + RegionView3D *rv3d = ss->cache->vc->rv3d; - test->radius_squared = ss->cache->radius_squared; - copy_v3_v3(test->location, ss->cache->location); - test->dist = 0.0f; /* just for initialize */ + test->radius_squared = ss->cache->radius_squared; + copy_v3_v3(test->location, ss->cache->location); + test->dist = 0.0f; /* just for initialize */ - /* Only for 2D projection. */ - zero_v4(test->plane_view); - zero_v4(test->plane_tool); + /* Only for 2D projection. */ + zero_v4(test->plane_view); + zero_v4(test->plane_tool); - test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; + test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; - if (rv3d->rflag & RV3D_CLIPPING) { - test->clip_rv3d = rv3d; - } - else { - test->clip_rv3d = NULL; - } + if (rv3d->rflag & RV3D_CLIPPING) { + test->clip_rv3d = rv3d; + } + else { + test->clip_rv3d = NULL; + } } BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) { - RegionView3D *rv3d = test->clip_rv3d; - if (!rv3d) { - return false; - } - float symm_co[3]; - flip_v3_v3(symm_co, co, test->mirror_symmetry_pass); - return ED_view3d_clipping_test(rv3d, symm_co, true); + RegionView3D *rv3d = test->clip_rv3d; + if (!rv3d) { + return false; + } + float symm_co[3]; + flip_v3_v3(symm_co, co, test->mirror_symmetry_pass); + return ED_view3d_clipping_test(rv3d, symm_co, true); } bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]) { - float distsq = len_squared_v3v3(co, test->location); + float distsq = len_squared_v3v3(co, test->location); - if (distsq <= test->radius_squared) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - test->dist = sqrtf(distsq); - return 1; - } - else { - return 0; - } + if (distsq <= test->radius_squared) { + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + test->dist = sqrtf(distsq); + return 1; + } + else { + return 0; + } } bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]) { - float distsq = len_squared_v3v3(co, test->location); + float distsq = len_squared_v3v3(co, test->location); - if (distsq <= test->radius_squared) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - test->dist = distsq; - return 1; - } - else { - return 0; - } + if (distsq <= test->radius_squared) { + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + test->dist = distsq; + return 1; + } + else { + return 0; + } } bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - return len_squared_v3v3(co, test->location) <= test->radius_squared; + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + return len_squared_v3v3(co, test->location) <= test->radius_squared; } bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]) { - float co_proj[3]; - closest_to_plane_normalized_v3(co_proj, test->plane_view, co); - float distsq = len_squared_v3v3(co_proj, test->location); + float co_proj[3]; + closest_to_plane_normalized_v3(co_proj, test->plane_view, co); + float distsq = len_squared_v3v3(co_proj, test->location); - if (distsq <= test->radius_squared) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - test->dist = distsq; - return 1; - } - else { - return 0; - } + if (distsq <= test->radius_squared) { + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + test->dist = distsq; + return 1; + } + else { + return 0; + } } bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]) { - float side = M_SQRT1_2; - float local_co[3]; + float side = M_SQRT1_2; + float local_co[3]; - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } - mul_v3_m4v3(local_co, local, co); + mul_v3_m4v3(local_co, local, co); - local_co[0] = fabsf(local_co[0]); - local_co[1] = fabsf(local_co[1]); - local_co[2] = fabsf(local_co[2]); + local_co[0] = fabsf(local_co[0]); + local_co[1] = fabsf(local_co[1]); + local_co[2] = fabsf(local_co[2]); - if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { - float p = 4.0f; + if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { + float p = 4.0f; - test->dist = ((powf(local_co[0], p) + - powf(local_co[1], p) + - powf(local_co[2], p)) / powf(side, p)); + test->dist = ((powf(local_co[0], p) + powf(local_co[1], p) + powf(local_co[2], p)) / + powf(side, p)); - return 1; - } - else { - return 0; - } + return 1; + } + else { + return 0; + } } -SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape( - SculptSession *ss, SculptBrushTest *test, char falloff_shape) +SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(SculptSession *ss, + SculptBrushTest *test, + char falloff_shape) { - sculpt_brush_test_init(ss, test); - SculptBrushTestFn sculpt_brush_test_sq_fn; - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq; - } - else { - /* PAINT_FALLOFF_SHAPE_TUBE */ - plane_from_point_normal_v3(test->plane_view, test->location, ss->cache->view_normal); - sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq; - } - return sculpt_brush_test_sq_fn; + sculpt_brush_test_init(ss, test); + SculptBrushTestFn sculpt_brush_test_sq_fn; + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq; + } + else { + /* PAINT_FALLOFF_SHAPE_TUBE */ + plane_from_point_normal_v3(test->plane_view, test->location, ss->cache->view_normal); + sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq; + } + return sculpt_brush_test_sq_fn; } -const float *sculpt_brush_frontface_normal_from_falloff_shape( - SculptSession *ss, char falloff_shape) +const float *sculpt_brush_frontface_normal_from_falloff_shape(SculptSession *ss, + char falloff_shape) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - return ss->cache->sculpt_normal_symm; - } - else { - /* PAINT_FALLOFF_SHAPE_TUBE */ - return ss->cache->view_normal; - } + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + return ss->cache->sculpt_normal_symm; + } + else { + /* PAINT_FALLOFF_SHAPE_TUBE */ + return ss->cache->view_normal; + } } - -static float frontface(const Brush *br, const float sculpt_normal[3], - const short no[3], const float fno[3]) +static float frontface(const Brush *br, + const float sculpt_normal[3], + const short no[3], + const float fno[3]) { - if (br->flag & BRUSH_FRONTFACE) { - float dot; + if (br->flag & BRUSH_FRONTFACE) { + float dot; - if (no) { - float tmp[3]; + if (no) { + float tmp[3]; - normal_short_to_float_v3(tmp, no); - dot = dot_v3v3(tmp, sculpt_normal); - } - else { - dot = dot_v3v3(fno, sculpt_normal); - } - return dot > 0 ? dot : 0; - } - else { - return 1; - } + normal_short_to_float_v3(tmp, no); + dot = dot_v3v3(tmp, sculpt_normal); + } + else { + dot = dot_v3v3(fno, sculpt_normal); + } + return dot > 0 ? dot : 0; + } + else { + return 1; + } } #if 0 static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], const float area_no[3]) { - if (sculpt_brush_test_sphere_fast(test, co)) { - float t1[3], t2[3], t3[3], dist; + if (sculpt_brush_test_sphere_fast(test, co)) { + float t1[3], t2[3], t3[3], dist; - sub_v3_v3v3(t1, location, co); - sub_v3_v3v3(t2, x2, location); + sub_v3_v3v3(t1, location, co); + sub_v3_v3v3(t2, x2, location); - cross_v3_v3v3(t3, area_no, t1); + cross_v3_v3v3(t3, area_no, t1); - dist = len_v3(t3) / len_v3(t2); + dist = len_v3(t3) / len_v3(t2); - test->dist = dist; + test->dist = dist; - return 1; - } + return 1; + } - return 0; + return 0; } #endif @@ -726,70 +702,73 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float loca */ static void flip_v3(float v[3], const char symm) { - flip_v3_v3(v, v, symm); + flip_v3_v3(v, v, symm); } static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) { - float mirror[3]; - float distsq; + float mirror[3]; + float distsq; - /* flip_v3_v3(mirror, cache->traced_location, symm); */ - flip_v3_v3(mirror, cache->true_location, symm); + /* flip_v3_v3(mirror, cache->traced_location, symm); */ + flip_v3_v3(mirror, cache->true_location, symm); - if (axis != 0) { - float mat[3][3]; - axis_angle_to_mat3_single(mat, axis, angle); - mul_m3_v3(mat, mirror); - } + if (axis != 0) { + float mat[3][3]; + axis_angle_to_mat3_single(mat, axis, angle); + mul_m3_v3(mat, mirror); + } - /* distsq = len_squared_v3v3(mirror, cache->traced_location); */ - distsq = len_squared_v3v3(mirror, cache->true_location); + /* distsq = len_squared_v3v3(mirror, cache->traced_location); */ + distsq = len_squared_v3v3(mirror, cache->true_location); - if (distsq <= 4.0f * (cache->radius_squared)) - return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius)); - else - return 0; + if (distsq <= 4.0f * (cache->radius_squared)) + return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius)); + else + return 0; } -static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis) +static float calc_radial_symmetry_feather(Sculpt *sd, + StrokeCache *cache, + const char symm, + const char axis) { - int i; - float overlap; + int i; + float overlap; - overlap = 0; - for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { - const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; - overlap += calc_overlap(cache, symm, axis, angle); - } + overlap = 0; + for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { + const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; + overlap += calc_overlap(cache, symm, axis, angle); + } - return overlap; + return overlap; } static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) { - if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { - float overlap; - int symm = cache->symmetry; - int i; + if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { + float overlap; + int symm = cache->symmetry; + int i; - overlap = 0; - for (i = 0; i <= symm; i++) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + overlap = 0; + for (i = 0; i <= symm; i++) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - overlap += calc_overlap(cache, i, 0, 0); + overlap += calc_overlap(cache, i, 0, 0); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); - } - } + overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); + overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); + overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); + } + } - return 1 / overlap; - } - else { - return 1; - } + return 1 / overlap; + } + else { + return 1; + } } /** \name Calculate Normal and Center @@ -805,393 +784,395 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) * \note These are all _very_ similar, when changing one, check others. * \{ */ -static void calc_area_normal_and_center_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - float (*area_nos)[3] = data->area_nos; - float (*area_cos)[3] = data->area_cos; - - PBVHVertexIter vd; - SculptUndoNode *unode = NULL; - - float private_co[2][3] = {{0.0f}}; - float private_no[2][3] = {{0.0f}}; - int private_count[2] = {0}; - bool use_original = false; - - if (ss->cache->original) { - unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); - use_original = (unode->co || unode->bm_entry); - } - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - - /* when the mesh is edited we can't rely on original coords - * (original mesh may not even have verts in brush radius) */ - if (use_original && data->has_bm_orco) { - float (*orco_coords)[3]; - int (*orco_tris)[3]; - int orco_tris_num; - int i; - - BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); - - for (i = 0; i < orco_tris_num; i++) { - const float *co_tri[3] = { - orco_coords[orco_tris[i][0]], - orco_coords[orco_tris[i][1]], - orco_coords[orco_tris[i][2]], - }; - float co[3]; - - closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); - - if (sculpt_brush_test_sq_fn(&test, co)) { - float no[3]; - int flip_index; - - normal_tri_v3(no, UNPACK3(co_tri)); - - flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); - if (area_cos) - add_v3_v3(private_co[flip_index], co); - if (area_nos) - add_v3_v3(private_no[flip_index], no); - private_count[flip_index] += 1; - } - } - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - const float *co; - const short *no_s; /* bm_vert only */ - - if (use_original) { - if (unode->bm_entry) { - BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); - } - else { - co = unode->co[vd.i]; - no_s = unode->no[vd.i]; - } - } - else { - co = vd.co; - } - - if (sculpt_brush_test_sq_fn(&test, co)) { - float no_buf[3]; - const float *no; - int flip_index; - - if (use_original) { - normal_short_to_float_v3(no_buf, no_s); - no = no_buf; - } - else { - if (vd.no) { - normal_short_to_float_v3(no_buf, vd.no); - no = no_buf; - } - else { - no = vd.fno; - } - } - - flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); - if (area_cos) - add_v3_v3(private_co[flip_index], co); - if (area_nos) - add_v3_v3(private_no[flip_index], no); - private_count[flip_index] += 1; - } - } - BKE_pbvh_vertex_iter_end; - } - - BLI_mutex_lock(&data->mutex); - - /* for flatten center */ - if (area_cos) { - add_v3_v3(area_cos[0], private_co[0]); - add_v3_v3(area_cos[1], private_co[1]); - } - - /* for area normal */ - if (area_nos) { - add_v3_v3(area_nos[0], private_no[0]); - add_v3_v3(area_nos[1], private_no[1]); - } - - /* weights */ - data->count[0] += private_count[0]; - data->count[1] += private_count[1]; - - BLI_mutex_unlock(&data->mutex); +static void calc_area_normal_and_center_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + float(*area_nos)[3] = data->area_nos; + float(*area_cos)[3] = data->area_cos; + + PBVHVertexIter vd; + SculptUndoNode *unode = NULL; + + float private_co[2][3] = {{0.0f}}; + float private_no[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original = false; + + if (ss->cache->original) { + unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + use_original = (unode->co || unode->bm_entry); + } + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && data->has_bm_orco) { + float(*orco_coords)[3]; + int(*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_sq_fn(&test, co)) { + float no[3]; + int flip_index; + + normal_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + if (area_cos) + add_v3_v3(private_co[flip_index], co); + if (area_nos) + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_sq_fn(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + if (area_cos) + add_v3_v3(private_co[flip_index], co); + if (area_nos) + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + + BLI_mutex_lock(&data->mutex); + + /* for flatten center */ + if (area_cos) { + add_v3_v3(area_cos[0], private_co[0]); + add_v3_v3(area_cos[1], private_co[1]); + } + + /* for area normal */ + if (area_nos) { + add_v3_v3(area_nos[0], private_no[0]); + add_v3_v3(area_nos[1], private_no[1]); + } + + /* weights */ + data->count[0] += private_count[0]; + data->count[1] += private_count[1]; + + BLI_mutex_unlock(&data->mutex); } static void calc_area_center( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_co[3]) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - int n; - - /* 0=towards view, 1=flipped */ - float area_cos[2][3] = {{0.0f}}; - - int count[2] = {0}; - - /* Intentionally set 'sd' to NULL since we share logic with vertex paint. */ - SculptThreadedTaskData data = { - .sd = NULL, .ob = ob, .brush = brush, .nodes = nodes, .totnode = totnode, - .has_bm_orco = has_bm_orco, .area_cos = area_cos, .area_nos = NULL, .count = count, - }; - BLI_mutex_init(&data.mutex); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - calc_area_normal_and_center_task_cb, - &settings); - - BLI_mutex_end(&data.mutex); - - /* for flatten center */ - for (n = 0; n < ARRAY_SIZE(area_cos); n++) { - if (count[n] != 0) { - mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); - break; - } - } - if (n == 2) { - zero_v3(r_area_co); - } + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_cos[2][3] = {{0.0f}}; + + int count[2] = {0}; + + /* Intentionally set 'sd' to NULL since we share logic with vertex paint. */ + SculptThreadedTaskData data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .has_bm_orco = has_bm_orco, + .area_cos = area_cos, + .area_nos = NULL, + .count = count, + }; + BLI_mutex_init(&data.mutex); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + + BLI_mutex_end(&data.mutex); + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_cos); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } } static void calc_area_normal( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3]) + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) { - const Brush *brush = BKE_paint_brush(&sd->paint); - bool use_threading = (sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT; - sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no); + const Brush *brush = BKE_paint_brush(&sd->paint); + bool use_threading = (sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT; + sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no); } /* expose 'calc_area_normal' externally. */ -void sculpt_pbvh_calc_area_normal( - const Brush *brush, Object *ob, - PBVHNode **nodes, int totnode, - bool use_threading, - float r_area_no[3]) -{ - SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - - /* 0=towards view, 1=flipped */ - float area_nos[2][3] = {{0.0f}}; - - int count[2] = {0}; - - /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ - SculptThreadedTaskData data = { - .sd = NULL, .ob = ob, .brush = brush, .nodes = nodes, .totnode = totnode, - .has_bm_orco = has_bm_orco, .area_cos = NULL, .area_nos = area_nos, .count = count, - }; - BLI_mutex_init(&data.mutex); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = use_threading; - BLI_task_parallel_range( - 0, totnode, - &data, - calc_area_normal_and_center_task_cb, - &settings); - - BLI_mutex_end(&data.mutex); - - /* for area normal */ - for (int i = 0; i < ARRAY_SIZE(area_nos); i++) { - if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) { - break; - } - } +void sculpt_pbvh_calc_area_normal(const Brush *brush, + Object *ob, + PBVHNode **nodes, + int totnode, + bool use_threading, + float r_area_no[3]) +{ + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + + /* 0=towards view, 1=flipped */ + float area_nos[2][3] = {{0.0f}}; + + int count[2] = {0}; + + /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ + SculptThreadedTaskData data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .has_bm_orco = has_bm_orco, + .area_cos = NULL, + .area_nos = area_nos, + .count = count, + }; + BLI_mutex_init(&data.mutex); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = use_threading; + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + + BLI_mutex_end(&data.mutex); + + /* for area normal */ + for (int i = 0; i < ARRAY_SIZE(area_nos); i++) { + if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) { + break; + } + } } - /* this calculates flatten center and area normal together, * amortizing the memory bandwidth and loop overhead to calculate both at the same time */ static void calc_area_normal_and_center( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3], float r_area_co[3]) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - int n; - - /* 0=towards view, 1=flipped */ - float area_cos[2][3] = {{0.0f}}; - float area_nos[2][3] = {{0.0f}}; - - int count[2] = {0}; - - /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ - SculptThreadedTaskData data = { - .sd = NULL, .ob = ob, .brush = brush, .nodes = nodes, .totnode = totnode, - .has_bm_orco = has_bm_orco, .area_cos = area_cos, .area_nos = area_nos, .count = count, - }; - BLI_mutex_init(&data.mutex); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - calc_area_normal_and_center_task_cb, - &settings); - - BLI_mutex_end(&data.mutex); - - /* for flatten center */ - for (n = 0; n < ARRAY_SIZE(area_cos); n++) { - if (count[n] != 0) { - mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); - break; - } - } - if (n == 2) { - zero_v3(r_area_co); - } - - /* for area normal */ - for (n = 0; n < ARRAY_SIZE(area_nos); n++) { - if (normalize_v3_v3(r_area_no, area_nos[n]) != 0.0f) { - break; - } - } + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_cos[2][3] = {{0.0f}}; + float area_nos[2][3] = {{0.0f}}; + + int count[2] = {0}; + + /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ + SculptThreadedTaskData data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .has_bm_orco = has_bm_orco, + .area_cos = area_cos, + .area_nos = area_nos, + .count = count, + }; + BLI_mutex_init(&data.mutex); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + + BLI_mutex_end(&data.mutex); + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_cos); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } + + /* for area normal */ + for (n = 0; n < ARRAY_SIZE(area_nos); n++) { + if (normalize_v3_v3(r_area_no, area_nos[n]) != 0.0f) { + break; + } + } } /** \} */ - /* Return modified brush strength. Includes the direction of the brush, positive * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -static float brush_strength( - const Sculpt *sd, const StrokeCache *cache, - const float feather, const UnifiedPaintSettings *ups) -{ - const Scene *scene = cache->vc->scene; - const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); - - /* Primary strength input; square it to make lower values more sensitive */ - const float root_alpha = BKE_brush_alpha_get(scene, brush); - float alpha = root_alpha * root_alpha; - float dir = (brush->flag & BRUSH_DIR_IN) ? -1 : 1; - float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; - float pen_flip = cache->pen_flip ? -1 : 1; - float invert = cache->invert ? -1 : 1; - float overlap = ups->overlap_factor; - /* spacing is integer percentage of radius, divide by 50 to get - * normalized diameter */ - - float flip = dir * invert * pen_flip; - - switch (brush->sculpt_tool) { - case SCULPT_TOOL_CLAY: - case SCULPT_TOOL_CLAY_STRIPS: - case SCULPT_TOOL_DRAW: - case SCULPT_TOOL_LAYER: - return alpha * flip * pressure * overlap * feather; - - case SCULPT_TOOL_MASK: - overlap = (1 + overlap) / 2; - switch ((BrushMaskTool)brush->mask_tool) { - case BRUSH_MASK_DRAW: - return alpha * flip * pressure * overlap * feather; - case BRUSH_MASK_SMOOTH: - return alpha * pressure * feather; - } - BLI_assert(!"Not supposed to happen"); - return 0.0f; - - case SCULPT_TOOL_CREASE: - case SCULPT_TOOL_BLOB: - return alpha * flip * pressure * overlap * feather; - - case SCULPT_TOOL_INFLATE: - if (flip > 0) { - return 0.250f * alpha * flip * pressure * overlap * feather; - } - else { - return 0.125f * alpha * flip * pressure * overlap * feather; - } - - case SCULPT_TOOL_FILL: - case SCULPT_TOOL_SCRAPE: - case SCULPT_TOOL_FLATTEN: - if (flip > 0) { - overlap = (1 + overlap) / 2; - return alpha * flip * pressure * overlap * feather; - } - else { - /* reduce strength for DEEPEN, PEAKS, and CONTRAST */ - return 0.5f * alpha * flip * pressure * overlap * feather; - } - - case SCULPT_TOOL_SMOOTH: - return alpha * pressure * feather; - - case SCULPT_TOOL_PINCH: - if (flip > 0) { - return alpha * flip * pressure * overlap * feather; - } - else { - return 0.25f * alpha * flip * pressure * overlap * feather; - } - - case SCULPT_TOOL_NUDGE: - overlap = (1 + overlap) / 2; - return alpha * pressure * overlap * feather; - - case SCULPT_TOOL_THUMB: - return alpha * pressure * feather; - - case SCULPT_TOOL_SNAKE_HOOK: - return root_alpha * feather; - - case SCULPT_TOOL_GRAB: - return root_alpha * feather; - - case SCULPT_TOOL_ROTATE: - return alpha * pressure * feather; - - default: - return 0; - } +static float brush_strength(const Sculpt *sd, + const StrokeCache *cache, + const float feather, + const UnifiedPaintSettings *ups) +{ + const Scene *scene = cache->vc->scene; + const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); + + /* Primary strength input; square it to make lower values more sensitive */ + const float root_alpha = BKE_brush_alpha_get(scene, brush); + float alpha = root_alpha * root_alpha; + float dir = (brush->flag & BRUSH_DIR_IN) ? -1 : 1; + float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; + float pen_flip = cache->pen_flip ? -1 : 1; + float invert = cache->invert ? -1 : 1; + float overlap = ups->overlap_factor; + /* spacing is integer percentage of radius, divide by 50 to get + * normalized diameter */ + + float flip = dir * invert * pen_flip; + + switch (brush->sculpt_tool) { + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_STRIPS: + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_LAYER: + return alpha * flip * pressure * overlap * feather; + + case SCULPT_TOOL_MASK: + overlap = (1 + overlap) / 2; + switch ((BrushMaskTool)brush->mask_tool) { + case BRUSH_MASK_DRAW: + return alpha * flip * pressure * overlap * feather; + case BRUSH_MASK_SMOOTH: + return alpha * pressure * feather; + } + BLI_assert(!"Not supposed to happen"); + return 0.0f; + + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + return alpha * flip * pressure * overlap * feather; + + case SCULPT_TOOL_INFLATE: + if (flip > 0) { + return 0.250f * alpha * flip * pressure * overlap * feather; + } + else { + return 0.125f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_FILL: + case SCULPT_TOOL_SCRAPE: + case SCULPT_TOOL_FLATTEN: + if (flip > 0) { + overlap = (1 + overlap) / 2; + return alpha * flip * pressure * overlap * feather; + } + else { + /* reduce strength for DEEPEN, PEAKS, and CONTRAST */ + return 0.5f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_SMOOTH: + return alpha * pressure * feather; + + case SCULPT_TOOL_PINCH: + if (flip > 0) { + return alpha * flip * pressure * overlap * feather; + } + else { + return 0.25f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_NUDGE: + overlap = (1 + overlap) / 2; + return alpha * pressure * overlap * feather; + + case SCULPT_TOOL_THUMB: + return alpha * pressure * feather; + + case SCULPT_TOOL_SNAKE_HOOK: + return root_alpha * feather; + + case SCULPT_TOOL_GRAB: + return root_alpha * feather; + + case SCULPT_TOOL_ROTATE: + return alpha * pressure * feather; + + default: + return 0; + } } /* Return a multiplier for brush strength on a particular vertex. */ -float tex_strength(SculptSession *ss, const Brush *br, +float tex_strength(SculptSession *ss, + const Brush *br, const float brush_point[3], const float len, const short vno[3], @@ -1199,299 +1180,295 @@ float tex_strength(SculptSession *ss, const Brush *br, const float mask, const int thread_id) { - StrokeCache *cache = ss->cache; - const Scene *scene = cache->vc->scene; - const MTex *mtex = &br->mtex; - float avg = 1; - float rgba[4]; - float point[3]; + StrokeCache *cache = ss->cache; + const Scene *scene = cache->vc->scene; + const MTex *mtex = &br->mtex; + float avg = 1; + float rgba[4]; + float point[3]; - sub_v3_v3v3(point, brush_point, cache->plane_offset); + sub_v3_v3v3(point, brush_point, cache->plane_offset); - if (!mtex->tex) { - avg = 1; - } - else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { - /* Get strength by feeding the vertex - * location directly into a texture */ - avg = BKE_brush_sample_tex_3d(scene, br, point, rgba, 0, ss->tex_pool); - } - else if (ss->texcache) { - float symm_point[3], point_2d[2]; - float x = 0.0f, y = 0.0f; /* Quite warnings */ + if (!mtex->tex) { + avg = 1; + } + else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + /* Get strength by feeding the vertex + * location directly into a texture */ + avg = BKE_brush_sample_tex_3d(scene, br, point, rgba, 0, ss->tex_pool); + } + else if (ss->texcache) { + float symm_point[3], point_2d[2]; + float x = 0.0f, y = 0.0f; /* Quite warnings */ - /* if the active area is being applied for symmetry, flip it - * across the symmetry axis and rotate it back to the original - * position in order to project it. This insures that the - * brush texture will be oriented correctly. */ + /* if the active area is being applied for symmetry, flip it + * across the symmetry axis and rotate it back to the original + * position in order to project it. This insures that the + * brush texture will be oriented correctly. */ - flip_v3_v3(symm_point, point, cache->mirror_symmetry_pass); + flip_v3_v3(symm_point, point, cache->mirror_symmetry_pass); - if (cache->radial_symmetry_pass) - mul_m4_v3(cache->symm_rot_mat_inv, symm_point); + if (cache->radial_symmetry_pass) + mul_m4_v3(cache->symm_rot_mat_inv, symm_point); - ED_view3d_project_float_v2_m4(cache->vc->ar, symm_point, point_2d, cache->projection_mat); + ED_view3d_project_float_v2_m4(cache->vc->ar, symm_point, point_2d, cache->projection_mat); - /* still no symmetry supported for other paint modes. - * Sculpt does it DIY */ - if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) { - /* Similar to fixed mode, but projects from brush angle - * rather than view direction */ + /* still no symmetry supported for other paint modes. + * Sculpt does it DIY */ + if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) { + /* Similar to fixed mode, but projects from brush angle + * rather than view direction */ - mul_m4_v3(cache->brush_local_mat, symm_point); + mul_m4_v3(cache->brush_local_mat, symm_point); - x = symm_point[0]; - y = symm_point[1]; + x = symm_point[0]; + y = symm_point[1]; - x *= br->mtex.size[0]; - y *= br->mtex.size[1]; + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; - x += br->mtex.ofs[0]; - y += br->mtex.ofs[1]; + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; - avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool, thread_id); + avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool, thread_id); - avg += br->texture_sample_bias; - } - else { - const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; - avg = BKE_brush_sample_tex_3d(scene, br, point_3d, rgba, 0, ss->tex_pool); - } - } + avg += br->texture_sample_bias; + } + else { + const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; + avg = BKE_brush_sample_tex_3d(scene, br, point_3d, rgba, 0, ss->tex_pool); + } + } - /* Falloff curve */ - avg *= BKE_brush_curve_strength(br, len, cache->radius); + /* Falloff curve */ + avg *= BKE_brush_curve_strength(br, len, cache->radius); - avg *= frontface(br, cache->view_normal, vno, fno); + avg *= frontface(br, cache->view_normal, vno, fno); - /* Paint mask */ - avg *= 1.0f - mask; + /* Paint mask */ + avg *= 1.0f - mask; - return avg; + return avg; } /* Test AABB against sphere */ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) { - SculptSearchSphereData *data = data_v; - float *center = data->ss->cache->location, nearest[3]; - float t[3], bb_min[3], bb_max[3]; - int i; + SculptSearchSphereData *data = data_v; + float *center = data->ss->cache->location, nearest[3]; + float t[3], bb_min[3], bb_max[3]; + int i; - if (data->original) - BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); - else - BKE_pbvh_node_get_BB(node, bb_min, bb_max); + if (data->original) + BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); + else + BKE_pbvh_node_get_BB(node, bb_min, bb_max); - for (i = 0; i < 3; ++i) { - if (bb_min[i] > center[i]) - nearest[i] = bb_min[i]; - else if (bb_max[i] < center[i]) - nearest[i] = bb_max[i]; - else - nearest[i] = center[i]; - } + for (i = 0; i < 3; ++i) { + if (bb_min[i] > center[i]) + nearest[i] = bb_min[i]; + else if (bb_max[i] < center[i]) + nearest[i] = bb_max[i]; + else + nearest[i] = center[i]; + } - sub_v3_v3v3(t, center, nearest); + sub_v3_v3v3(t, center, nearest); - return len_squared_v3(t) < data->radius_squared; + return len_squared_v3(t) < data->radius_squared; } /* 2D projection (distance to line). */ bool sculpt_search_circle_cb(PBVHNode *node, void *data_v) { - SculptSearchCircleData *data = data_v; - float bb_min[3], bb_max[3]; + SculptSearchCircleData *data = data_v; + float bb_min[3], bb_max[3]; - if (data->original) - BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); - else - BKE_pbvh_node_get_BB(node, bb_min, bb_min); + if (data->original) + BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); + else + BKE_pbvh_node_get_BB(node, bb_min, bb_min); - float dummy_co[3], dummy_depth; - const float dist_sq = dist_squared_ray_to_aabb_v3( - data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth); + float dummy_co[3], dummy_depth; + const float dist_sq = dist_squared_ray_to_aabb_v3( + data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth); - return dist_sq < data->radius_squared || 1; + return dist_sq < data->radius_squared || 1; } /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3]) { - int i; - - for (i = 0; i < 3; ++i) { - if (sd->flags & (SCULPT_LOCK_X << i)) - continue; - - if ((ss->cache->flag & (CLIP_X << i)) && (fabsf(co[i]) <= ss->cache->clip_tolerance[i])) - co[i] = 0.0f; - else - co[i] = val[i]; - } -} - -static PBVHNode **sculpt_pbvh_gather_generic( - Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius_scale, int *r_totnode) -{ - SculptSession *ss = ob->sculpt; - PBVHNode **nodes = NULL; - - /* Build a list of all nodes that are potentially within the brush's area of influence */ - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), - .original = use_original, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); - } - else { - struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; - dist_squared_ray_to_aabb_v3_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); - SculptSearchCircleData data = { - .ss = ss, - .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), - .original = use_original, - .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); - } - return nodes; + int i; + + for (i = 0; i < 3; ++i) { + if (sd->flags & (SCULPT_LOCK_X << i)) + continue; + + if ((ss->cache->flag & (CLIP_X << i)) && (fabsf(co[i]) <= ss->cache->clip_tolerance[i])) + co[i] = 0.0f; + else + co[i] = val[i]; + } +} + +static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, + Sculpt *sd, + const Brush *brush, + bool use_original, + float radius_scale, + int *r_totnode) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes = NULL; + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .original = use_original, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); + } + else { + struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; + dist_squared_ray_to_aabb_v3_precalc( + &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); + SculptSearchCircleData data = { + .ss = ss, + .sd = sd, + .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .original = use_original, + .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); + } + return nodes; } /* Calculate primary direction of movement for many brushes */ static void calc_sculpt_normal( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3]) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - const SculptSession *ss = ob->sculpt; - - switch (brush->sculpt_plane) { - case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(r_area_no, ss->cache->true_view_normal); - break; - - case SCULPT_DISP_DIR_X: - ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); - break; - - case SCULPT_DISP_DIR_Y: - ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); - break; - - case SCULPT_DISP_DIR_Z: - ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); - break; - - case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ob, nodes, totnode, r_area_no); - break; - - default: - break; - } -} - -static void update_sculpt_normal(Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - StrokeCache *cache = ob->sculpt->cache; - - if (cache->mirror_symmetry_pass == 0 && - cache->radial_symmetry_pass == 0 && - (cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) - { - calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(cache->sculpt_normal, cache->sculpt_normal, cache->view_normal); - normalize_v3(cache->sculpt_normal); - } - copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); - } - else { - copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); - flip_v3(cache->sculpt_normal_symm, cache->mirror_symmetry_pass); - mul_m4_v3(cache->symm_rot_mat, cache->sculpt_normal_symm); - } + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + const SculptSession *ss = ob->sculpt; + + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + copy_v3_v3(r_area_no, ss->cache->true_view_normal); + break; + + case SCULPT_DISP_DIR_X: + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); + break; + + case SCULPT_DISP_DIR_Y: + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); + break; + + case SCULPT_DISP_DIR_Z: + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); + break; + + case SCULPT_DISP_DIR_AREA: + calc_area_normal(sd, ob, nodes, totnode, r_area_no); + break; + + default: + break; + } +} + +static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + StrokeCache *cache = ob->sculpt->cache; + + if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0 && + (cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(cache->sculpt_normal, cache->sculpt_normal, cache->view_normal); + normalize_v3(cache->sculpt_normal); + } + copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); + } + else { + copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); + flip_v3(cache->sculpt_normal_symm, cache->mirror_symmetry_pass); + mul_m4_v3(cache->symm_rot_mat, cache->sculpt_normal_symm); + } } static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) { - Object *ob = vc->obact; - float loc[3], mval_f[2] = {0.0f, 1.0f}; - float zfac; + Object *ob = vc->obact; + float loc[3], mval_f[2] = {0.0f, 1.0f}; + float zfac; - mul_v3_m4v3(loc, ob->imat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); + mul_v3_m4v3(loc, ob->imat, center); + zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); - ED_view3d_win_to_delta(vc->ar, mval_f, y, zfac); - normalize_v3(y); + ED_view3d_win_to_delta(vc->ar, mval_f, y, zfac); + normalize_v3(y); - add_v3_v3(y, ob->loc); - mul_m4_v3(ob->imat, y); + add_v3_v3(y, ob->loc); + mul_m4_v3(ob->imat, y); } -static void calc_brush_local_mat(const Brush *brush, Object *ob, - float local_mat[4][4]) +static void calc_brush_local_mat(const Brush *brush, Object *ob, float local_mat[4][4]) { - const StrokeCache *cache = ob->sculpt->cache; - float tmat[4][4]; - float mat[4][4]; - float scale[4][4]; - float angle, v[3]; - float up[3]; + const StrokeCache *cache = ob->sculpt->cache; + float tmat[4][4]; + float mat[4][4]; + float scale[4][4]; + float angle, v[3]; + float up[3]; - /* Ensure ob->imat is up to date */ - invert_m4_m4(ob->imat, ob->obmat); + /* Ensure ob->imat is up to date */ + invert_m4_m4(ob->imat, ob->obmat); - /* Initialize last column of matrix */ - mat[0][3] = 0; - mat[1][3] = 0; - mat[2][3] = 0; - mat[3][3] = 1; + /* Initialize last column of matrix */ + mat[0][3] = 0; + mat[1][3] = 0; + mat[2][3] = 0; + mat[3][3] = 1; - /* Get view's up vector in object-space */ - calc_local_y(cache->vc, cache->location, up); + /* Get view's up vector in object-space */ + calc_local_y(cache->vc, cache->location, up); - /* Calculate the X axis of the local matrix */ - cross_v3_v3v3(v, up, cache->sculpt_normal); - /* Apply rotation (user angle, rake, etc.) to X axis */ - angle = brush->mtex.rot - cache->special_rotation; - rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle); + /* Calculate the X axis of the local matrix */ + cross_v3_v3v3(v, up, cache->sculpt_normal); + /* Apply rotation (user angle, rake, etc.) to X axis */ + angle = brush->mtex.rot - cache->special_rotation; + rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle); - /* Get other axes */ - cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]); - copy_v3_v3(mat[2], cache->sculpt_normal); + /* Get other axes */ + cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]); + copy_v3_v3(mat[2], cache->sculpt_normal); - /* Set location */ - copy_v3_v3(mat[3], cache->location); + /* Set location */ + copy_v3_v3(mat[3], cache->location); - /* Scale by brush radius */ - normalize_m4(mat); - scale_m4_fl(scale, cache->radius); - mul_m4_m4m4(tmat, mat, scale); + /* Scale by brush radius */ + normalize_m4(mat); + scale_m4_fl(scale, cache->radius); + mul_m4_m4m4(tmat, mat, scale); - /* Return inverse (for converting from modelspace coords to local - * area coords) */ - invert_m4_m4(local_mat, tmat); + /* Return inverse (for converting from modelspace coords to local + * area coords) */ + invert_m4_m4(local_mat, tmat); } static void update_brush_local_mat(Sculpt *sd, Object *ob) { - StrokeCache *cache = ob->sculpt->cache; + StrokeCache *cache = ob->sculpt->cache; - if (cache->mirror_symmetry_pass == 0 && - cache->radial_symmetry_pass == 0) - { - calc_brush_local_mat(BKE_paint_brush(&sd->paint), ob, - cache->brush_local_mat); - } + if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0) { + calc_brush_local_mat(BKE_paint_brush(&sd->paint), ob, cache->brush_local_mat); + } } /* For the smooth brush, uses the neighboring vertices around vert to calculate @@ -1499,40 +1476,39 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) * polygon.) */ static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert) { - const MeshElemMap *vert_map = &ss->pmap[vert]; - const MVert *mvert = ss->mvert; - float (*deform_co)[3] = ss->deform_cos; + const MeshElemMap *vert_map = &ss->pmap[vert]; + const MVert *mvert = ss->mvert; + float(*deform_co)[3] = ss->deform_cos; - /* Don't modify corner vertices */ - if (vert_map->count > 1) { - int i, total = 0; + /* Don't modify corner vertices */ + if (vert_map->count > 1) { + int i, total = 0; - zero_v3(avg); + zero_v3(avg); - for (i = 0; i < vert_map->count; i++) { - const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - unsigned f_adj_v[2]; + for (i = 0; i < vert_map->count; i++) { + const MPoly *p = &ss->mpoly[vert_map->indices[i]]; + unsigned f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { - int j; - for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { - add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : - mvert[f_adj_v[j]].co); + if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { + int j; + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { + if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { + add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : mvert[f_adj_v[j]].co); - total++; - } - } - } - } + total++; + } + } + } + } - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - return; - } - } + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + return; + } + } - copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co); + copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co); } /* Similar to neighbor_average(), but returns an averaged mask value @@ -1540,2664 +1516,2727 @@ static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert) * corner vertices. */ static float neighbor_average_mask(SculptSession *ss, unsigned vert) { - const float *vmask = ss->vmask; - float avg = 0; - int i, total = 0; + const float *vmask = ss->vmask; + float avg = 0; + int i, total = 0; - for (i = 0; i < ss->pmap[vert].count; i++) { - const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]]; - unsigned f_adj_v[2]; + for (i = 0; i < ss->pmap[vert].count; i++) { + const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]]; + unsigned f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { - int j; - for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - avg += vmask[f_adj_v[j]]; - total++; - } - } - } + if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { + int j; + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { + avg += vmask[f_adj_v[j]]; + total++; + } + } + } - if (total > 0) - return avg / (float)total; - else - return vmask[vert]; + if (total > 0) + return avg / (float)total; + else + return vmask[vert]; } /* Same logic as neighbor_average(), but for bmesh rather than mesh */ static void bmesh_neighbor_average(float avg[3], BMVert *v) { - /* logic for 3 or more is identical */ - const int vfcount = BM_vert_face_count_at_most(v, 3); + /* logic for 3 or more is identical */ + const int vfcount = BM_vert_face_count_at_most(v, 3); - /* Don't modify corner vertices */ - if (vfcount > 1) { - BMIter liter; - BMLoop *l; - int i, total = 0; + /* Don't modify corner vertices */ + if (vfcount > 1) { + BMIter liter; + BMLoop *l; + int i, total = 0; - zero_v3(avg); + zero_v3(avg); - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - const BMVert *adj_v[2] = {l->prev->v, l->next->v}; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < ARRAY_SIZE(adj_v); i++) { - const BMVert *v_other = adj_v[i]; - if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { - add_v3_v3(avg, v_other->co); - total++; - } - } - } + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { + add_v3_v3(avg, v_other->co); + total++; + } + } + } - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - return; - } - } + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + return; + } + } - copy_v3_v3(avg, v->co); + copy_v3_v3(avg, v->co); } /* For bmesh: average only the four most aligned (parallel and perpendicular) edges * relative to a direction. Naturally converges to a quad-like tessellation. */ static void bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v) { - /* Logic for 3 or more is identical. */ - const int vfcount = BM_vert_face_count_at_most(v, 3); - - /* Don't modify corner vertices. */ - if (vfcount < 2) { - copy_v3_v3(avg, v->co); - return; - } - - /* Project the direction to the vertex normal and create an additional - * parallel vector. */ - float dir_a[3], dir_b[3]; - cross_v3_v3v3(dir_a, direction, v->no); - cross_v3_v3v3(dir_b, dir_a, v->no); - - /* The four vectors which will be used for smoothing. - * Occasionally less than 4 verts match the requirements in that case - * use 'v' as fallback. */ - BMVert *pos_a = v; - BMVert *neg_a = v; - BMVert *pos_b = v; - BMVert *neg_b = v; - - float pos_score_a = 0.0f; - float neg_score_a = 0.0f; - float pos_score_b = 0.0f; - float neg_score_b = 0.0f; - - BMIter liter; - BMLoop *l; - - BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[2] = { l->prev->v, l->next->v }; - - for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { - BMVert *v_other = adj_v[i]; - - if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { - float vec[3]; - sub_v3_v3v3(vec, v_other->co, v->co); - normalize_v3(vec); - - /* The score is a measure of how orthogonal the edge is. */ - float score = dot_v3v3(vec, dir_a); - - if (score >= pos_score_a) { - pos_a = v_other; - pos_score_a = score; - } - else if (score < neg_score_a) { - neg_a = v_other; - neg_score_a = score; - } - /* The same scoring but for the perpendicular direction. */ - score = dot_v3v3(vec, dir_b); - - if (score >= pos_score_b) { - pos_b = v_other; - pos_score_b = score; - } - else if (score < neg_score_b) { - neg_b = v_other; - neg_score_b = score; - } - } - } - } - - /* Average everything together. */ - zero_v3(avg); - add_v3_v3(avg, pos_a->co); - add_v3_v3(avg, neg_a->co); - add_v3_v3(avg, pos_b->co); - add_v3_v3(avg, neg_b->co); - mul_v3_fl(avg, 0.25f); - - /* Preserve volume. */ - float vec[3]; - sub_v3_v3(avg, v->co); - mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); - sub_v3_v3(avg, vec); - add_v3_v3(avg, v->co); + /* Logic for 3 or more is identical. */ + const int vfcount = BM_vert_face_count_at_most(v, 3); + + /* Don't modify corner vertices. */ + if (vfcount < 2) { + copy_v3_v3(avg, v->co); + return; + } + + /* Project the direction to the vertex normal and create an additional + * parallel vector. */ + float dir_a[3], dir_b[3]; + cross_v3_v3v3(dir_a, direction, v->no); + cross_v3_v3v3(dir_b, dir_a, v->no); + + /* The four vectors which will be used for smoothing. + * Occasionally less than 4 verts match the requirements in that case + * use 'v' as fallback. */ + BMVert *pos_a = v; + BMVert *neg_a = v; + BMVert *pos_b = v; + BMVert *neg_b = v; + + float pos_score_a = 0.0f; + float neg_score_a = 0.0f; + float pos_score_b = 0.0f; + float neg_score_b = 0.0f; + + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + BMVert *adj_v[2] = {l->prev->v, l->next->v}; + + for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { + BMVert *v_other = adj_v[i]; + + if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { + float vec[3]; + sub_v3_v3v3(vec, v_other->co, v->co); + normalize_v3(vec); + + /* The score is a measure of how orthogonal the edge is. */ + float score = dot_v3v3(vec, dir_a); + + if (score >= pos_score_a) { + pos_a = v_other; + pos_score_a = score; + } + else if (score < neg_score_a) { + neg_a = v_other; + neg_score_a = score; + } + /* The same scoring but for the perpendicular direction. */ + score = dot_v3v3(vec, dir_b); + + if (score >= pos_score_b) { + pos_b = v_other; + pos_score_b = score; + } + else if (score < neg_score_b) { + neg_b = v_other; + neg_score_b = score; + } + } + } + } + + /* Average everything together. */ + zero_v3(avg); + add_v3_v3(avg, pos_a->co); + add_v3_v3(avg, neg_a->co); + add_v3_v3(avg, pos_b->co); + add_v3_v3(avg, neg_b->co); + mul_v3_fl(avg, 0.25f); + + /* Preserve volume. */ + float vec[3]; + sub_v3_v3(avg, v->co); + mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); + sub_v3_v3(avg, vec); + add_v3_v3(avg, v->co); } /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ static float bmesh_neighbor_average_mask(BMVert *v, const int cd_vert_mask_offset) { - BMIter liter; - BMLoop *l; - float avg = 0; - int i, total = 0; + BMIter liter; + BMLoop *l; + float avg = 0; + int i, total = 0; - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - /* skip this vertex */ - const BMVert *adj_v[2] = {l->prev->v, l->next->v}; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + /* skip this vertex */ + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < ARRAY_SIZE(adj_v); i++) { - const BMVert *v_other = adj_v[i]; - const float *vmask = BM_ELEM_CD_GET_VOID_P(v_other, cd_vert_mask_offset); - avg += (*vmask); - total++; - } - } + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + const float *vmask = BM_ELEM_CD_GET_VOID_P(v_other, cd_vert_mask_offset); + avg += (*vmask); + total++; + } + } - if (total > 0) { - return avg / (float)total; - } - else { - const float *vmask = BM_ELEM_CD_GET_VOID_P(v, cd_vert_mask_offset); - return (*vmask); - } + if (total > 0) { + return avg / (float)total; + } + else { + const float *vmask = BM_ELEM_CD_GET_VOID_P(v, cd_vert_mask_offset); + return (*vmask); + } } /* Note: uses after-struct allocated mem to store actual cache... */ typedef struct SculptDoBrushSmoothGridDataChunk { - size_t tmpgrid_size; + size_t tmpgrid_size; } SculptDoBrushSmoothGridDataChunk; typedef struct { - SculptSession *ss; - const float *ray_start, *ray_normal; - bool hit; - float depth; - bool original; + SculptSession *ss; + const float *ray_start, *ray_normal; + bool hit; + float depth; + bool original; } SculptRaycastData; typedef struct { - const float *ray_start, *ray_normal; - bool hit; - float depth; - float edge_length; + const float *ray_start, *ray_normal; + bool hit; + float depth; + float edge_length; } SculptDetailRaycastData; typedef struct { - SculptSession *ss; - const float *ray_start, *ray_normal; - bool hit; - float depth; - float dist_sq_to_ray; - bool original; + SculptSession *ss; + const float *ray_start, *ray_normal; + bool hit; + float depth; + float dist_sq_to_ray; + bool original; } SculptFindNearestToRayData; -static void do_smooth_brush_mesh_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const bool smooth_mask = data->smooth_mask; - float bstrength = data->strength; - - PBVHVertexIter vd; - - CLAMP(bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), tls->thread_id); - if (smooth_mask) { - float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask; - val *= fade * bstrength; - *vd.mask += val; - CLAMP(*vd.mask, 0.0f, 1.0f); - } - else { - float avg[3], val[3]; - - neighbor_average(ss, avg, vd.vert_indices[vd.i]); - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - sculpt_clip(sd, ss, vd.co, val); - } - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_smooth_brush_bmesh_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const bool smooth_mask = data->smooth_mask; - float bstrength = data->strength; - - PBVHVertexIter vd; - - CLAMP(bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, smooth_mask ? 0.0f : *vd.mask, tls->thread_id); - if (smooth_mask) { - float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; - val *= fade * bstrength; - *vd.mask += val; - CLAMP(*vd.mask, 0.0f, 1.0f); - } - else { - float avg[3], val[3]; - - bmesh_neighbor_average(avg, vd.bm_vert); - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - sculpt_clip(sd, ss, vd.co, val); - } - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_topology_rake_bmesh_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - - float direction[3]; - copy_v3_v3(direction, ss->cache->grab_delta_symmetry); - - float tmp[3]; - mul_v3_v3fl( - tmp, ss->cache->sculpt_normal_symm, - dot_v3v3(ss->cache->sculpt_normal_symm, direction)); - sub_v3_v3(direction, tmp); - - /* Cancel if there's no grab data. */ - if (is_zero_v3(direction)) { - return; - } - - float bstrength = data->strength; - CLAMP(bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, *vd.mask, tls->thread_id) * ss->cache->pressure; - - float avg[3], val[3]; - - bmesh_four_neighbor_average(avg, direction, vd.bm_vert); - - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - sculpt_clip(sd, ss, vd.co, val); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_smooth_brush_multires_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptDoBrushSmoothGridDataChunk *data_chunk = tls->userdata_chunk; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const bool smooth_mask = data->smooth_mask; - float bstrength = data->strength; - - CCGElem **griddata, *gddata; - CCGKey key; - - float (*tmpgrid_co)[3] = NULL; - float tmprow_co[2][3]; - float *tmpgrid_mask = NULL; - float tmprow_mask[2]; - - BLI_bitmap * const *grid_hidden; - int *grid_indices, totgrid, gridsize; - int i, x, y; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - CLAMP(bstrength, 0.0f, 1.0f); - - BKE_pbvh_node_get_grids(ss->pbvh, data->nodes[n], &grid_indices, &totgrid, NULL, &gridsize, &griddata); - BKE_pbvh_get_grid_key(ss->pbvh, &key); - - grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); - - if (smooth_mask) - tmpgrid_mask = (void *)(data_chunk + 1); - else - tmpgrid_co = (void *)(data_chunk + 1); - - for (i = 0; i < totgrid; i++) { - int gi = grid_indices[i]; - const BLI_bitmap *gh = grid_hidden[gi]; - gddata = griddata[gi]; - - if (smooth_mask) - memset(tmpgrid_mask, 0, data_chunk->tmpgrid_size); - else - memset(tmpgrid_co, 0, data_chunk->tmpgrid_size); - - for (y = 0; y < gridsize - 1; y++) { - const int v = y * gridsize; - if (smooth_mask) { - tmprow_mask[0] = (*CCG_elem_offset_mask(&key, gddata, v) + - *CCG_elem_offset_mask(&key, gddata, v + gridsize)); - } - else { - add_v3_v3v3(tmprow_co[0], - CCG_elem_offset_co(&key, gddata, v), - CCG_elem_offset_co(&key, gddata, v + gridsize)); - } - - for (x = 0; x < gridsize - 1; x++) { - const int v1 = x + y * gridsize; - const int v2 = v1 + 1; - const int v3 = v1 + gridsize; - const int v4 = v3 + 1; - - if (smooth_mask) { - float tmp; - - tmprow_mask[(x + 1) % 2] = (*CCG_elem_offset_mask(&key, gddata, v2) + - *CCG_elem_offset_mask(&key, gddata, v4)); - tmp = tmprow_mask[(x + 1) % 2] + tmprow_mask[x % 2]; - - tmpgrid_mask[v1] += tmp; - tmpgrid_mask[v2] += tmp; - tmpgrid_mask[v3] += tmp; - tmpgrid_mask[v4] += tmp; - } - else { - float tmp[3]; - - add_v3_v3v3(tmprow_co[(x + 1) % 2], - CCG_elem_offset_co(&key, gddata, v2), - CCG_elem_offset_co(&key, gddata, v4)); - add_v3_v3v3(tmp, tmprow_co[(x + 1) % 2], tmprow_co[x % 2]); - - add_v3_v3(tmpgrid_co[v1], tmp); - add_v3_v3(tmpgrid_co[v2], tmp); - add_v3_v3(tmpgrid_co[v3], tmp); - add_v3_v3(tmpgrid_co[v4], tmp); - } - } - } - - /* blend with existing coordinates */ - for (y = 0; y < gridsize; y++) { - for (x = 0; x < gridsize; x++) { - float *co; - const float *fno; - float *mask; - const int index = y * gridsize + x; - - if (gh) { - if (BLI_BITMAP_TEST(gh, index)) - continue; - } - - co = CCG_elem_offset_co(&key, gddata, index); - fno = CCG_elem_offset_no(&key, gddata, index); - mask = CCG_elem_offset_mask(&key, gddata, index); - - if (sculpt_brush_test_sq_fn(&test, co)) { - const float strength_mask = (smooth_mask ? 0.0f : *mask); - const float fade = bstrength * tex_strength( - ss, brush, co, sqrtf(test.dist), - NULL, fno, strength_mask, tls->thread_id); - float f = 1.0f / 16.0f; - - if (x == 0 || x == gridsize - 1) - f *= 2.0f; - - if (y == 0 || y == gridsize - 1) - f *= 2.0f; - - if (smooth_mask) { - *mask += ((tmpgrid_mask[index] * f) - *mask) * fade; - } - else { - float *avg = tmpgrid_co[index]; - float val[3]; - - mul_v3_fl(avg, f); - sub_v3_v3v3(val, avg, co); - madd_v3_v3v3fl(val, co, val, fade); - - sculpt_clip(sd, ss, co, val); - } - } - } - } - } -} - -static void smooth( - Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength, const bool smooth_mask) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const int max_iterations = 4; - const float fract = 1.0f / max_iterations; - PBVHType type = BKE_pbvh_type(ss->pbvh); - int iteration, count; - float last; - - CLAMP(bstrength, 0.0f, 1.0f); - - count = (int)(bstrength * max_iterations); - last = max_iterations * (bstrength - count * fract); - - if (type == PBVH_FACES && !ss->pmap) { - BLI_assert(!"sculpt smooth: pmap missing"); - return; - } - - for (iteration = 0; iteration <= count; ++iteration) { - const float strength = (iteration != count) ? 1.0f : last; - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .smooth_mask = smooth_mask, .strength = strength, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - - switch (type) { - case PBVH_GRIDS: - { - int gridsize; - size_t size; - SculptDoBrushSmoothGridDataChunk *data_chunk; - - BKE_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL, &gridsize, NULL); - size = (size_t)gridsize; - size = sizeof(float) * size * size * (smooth_mask ? 1 : 3); - data_chunk = MEM_mallocN(sizeof(*data_chunk) + size, __func__); - data_chunk->tmpgrid_size = size; - size += sizeof(*data_chunk); - - settings.userdata_chunk = data_chunk; - settings.userdata_chunk_size = size; - BLI_task_parallel_range( - 0, totnode, - &data, - do_smooth_brush_multires_task_cb_ex, - &settings); - - MEM_freeN(data_chunk); - break; - } - case PBVH_FACES: - BLI_task_parallel_range( - 0, totnode, - &data, - do_smooth_brush_mesh_task_cb_ex, - &settings); - break; - case PBVH_BMESH: - BLI_task_parallel_range( - 0, totnode, - &data, - do_smooth_brush_bmesh_task_cb_ex, - &settings); - break; - } - - if (ss->multires) - multires_stitch_grids(ob); - } +static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + tls->thread_id); + if (smooth_mask) { + float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask; + val *= fade * bstrength; + *vd.mask += val; + CLAMP(*vd.mask, 0.0f, 1.0f); + } + else { + float avg[3], val[3]; + + neighbor_average(ss, avg, vd.vert_indices[vd.i]); + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + sculpt_clip(sd, ss, vd.co, val); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : *vd.mask, + tls->thread_id); + if (smooth_mask) { + float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; + val *= fade * bstrength; + *vd.mask += val; + CLAMP(*vd.mask, 0.0f, 1.0f); + } + else { + float avg[3], val[3]; + + bmesh_neighbor_average(avg, vd.bm_vert); + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + sculpt_clip(sd, ss, vd.co, val); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + + float direction[3]; + copy_v3_v3(direction, ss->cache->grab_delta_symmetry); + + float tmp[3]; + mul_v3_v3fl( + tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction)); + sub_v3_v3(direction, tmp); + + /* Cancel if there's no grab data. */ + if (is_zero_v3(direction)) { + return; + } + + float bstrength = data->strength; + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = + bstrength * + tex_strength( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, tls->thread_id) * + ss->cache->pressure; + + float avg[3], val[3]; + + bmesh_four_neighbor_average(avg, direction, vd.bm_vert); + + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + sculpt_clip(sd, ss, vd.co, val); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptDoBrushSmoothGridDataChunk *data_chunk = tls->userdata_chunk; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + CCGElem **griddata, *gddata; + CCGKey key; + + float(*tmpgrid_co)[3] = NULL; + float tmprow_co[2][3]; + float *tmpgrid_mask = NULL; + float tmprow_mask[2]; + + BLI_bitmap *const *grid_hidden; + int *grid_indices, totgrid, gridsize; + int i, x, y; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + CLAMP(bstrength, 0.0f, 1.0f); + + BKE_pbvh_node_get_grids( + ss->pbvh, data->nodes[n], &grid_indices, &totgrid, NULL, &gridsize, &griddata); + BKE_pbvh_get_grid_key(ss->pbvh, &key); + + grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); + + if (smooth_mask) + tmpgrid_mask = (void *)(data_chunk + 1); + else + tmpgrid_co = (void *)(data_chunk + 1); + + for (i = 0; i < totgrid; i++) { + int gi = grid_indices[i]; + const BLI_bitmap *gh = grid_hidden[gi]; + gddata = griddata[gi]; + + if (smooth_mask) + memset(tmpgrid_mask, 0, data_chunk->tmpgrid_size); + else + memset(tmpgrid_co, 0, data_chunk->tmpgrid_size); + + for (y = 0; y < gridsize - 1; y++) { + const int v = y * gridsize; + if (smooth_mask) { + tmprow_mask[0] = (*CCG_elem_offset_mask(&key, gddata, v) + + *CCG_elem_offset_mask(&key, gddata, v + gridsize)); + } + else { + add_v3_v3v3(tmprow_co[0], + CCG_elem_offset_co(&key, gddata, v), + CCG_elem_offset_co(&key, gddata, v + gridsize)); + } + + for (x = 0; x < gridsize - 1; x++) { + const int v1 = x + y * gridsize; + const int v2 = v1 + 1; + const int v3 = v1 + gridsize; + const int v4 = v3 + 1; + + if (smooth_mask) { + float tmp; + + tmprow_mask[(x + 1) % 2] = (*CCG_elem_offset_mask(&key, gddata, v2) + + *CCG_elem_offset_mask(&key, gddata, v4)); + tmp = tmprow_mask[(x + 1) % 2] + tmprow_mask[x % 2]; + + tmpgrid_mask[v1] += tmp; + tmpgrid_mask[v2] += tmp; + tmpgrid_mask[v3] += tmp; + tmpgrid_mask[v4] += tmp; + } + else { + float tmp[3]; + + add_v3_v3v3(tmprow_co[(x + 1) % 2], + CCG_elem_offset_co(&key, gddata, v2), + CCG_elem_offset_co(&key, gddata, v4)); + add_v3_v3v3(tmp, tmprow_co[(x + 1) % 2], tmprow_co[x % 2]); + + add_v3_v3(tmpgrid_co[v1], tmp); + add_v3_v3(tmpgrid_co[v2], tmp); + add_v3_v3(tmpgrid_co[v3], tmp); + add_v3_v3(tmpgrid_co[v4], tmp); + } + } + } + + /* blend with existing coordinates */ + for (y = 0; y < gridsize; y++) { + for (x = 0; x < gridsize; x++) { + float *co; + const float *fno; + float *mask; + const int index = y * gridsize + x; + + if (gh) { + if (BLI_BITMAP_TEST(gh, index)) + continue; + } + + co = CCG_elem_offset_co(&key, gddata, index); + fno = CCG_elem_offset_no(&key, gddata, index); + mask = CCG_elem_offset_mask(&key, gddata, index); + + if (sculpt_brush_test_sq_fn(&test, co)) { + const float strength_mask = (smooth_mask ? 0.0f : *mask); + const float fade = + bstrength * + tex_strength( + ss, brush, co, sqrtf(test.dist), NULL, fno, strength_mask, tls->thread_id); + float f = 1.0f / 16.0f; + + if (x == 0 || x == gridsize - 1) + f *= 2.0f; + + if (y == 0 || y == gridsize - 1) + f *= 2.0f; + + if (smooth_mask) { + *mask += ((tmpgrid_mask[index] * f) - *mask) * fade; + } + else { + float *avg = tmpgrid_co[index]; + float val[3]; + + mul_v3_fl(avg, f); + sub_v3_v3v3(val, avg, co); + madd_v3_v3v3fl(val, co, val, fade); + + sculpt_clip(sd, ss, co, val); + } + } + } + } + } +} + +static void smooth(Sculpt *sd, + Object *ob, + PBVHNode **nodes, + const int totnode, + float bstrength, + const bool smooth_mask) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const int max_iterations = 4; + const float fract = 1.0f / max_iterations; + PBVHType type = BKE_pbvh_type(ss->pbvh); + int iteration, count; + float last; + + CLAMP(bstrength, 0.0f, 1.0f); + + count = (int)(bstrength * max_iterations); + last = max_iterations * (bstrength - count * fract); + + if (type == PBVH_FACES && !ss->pmap) { + BLI_assert(!"sculpt smooth: pmap missing"); + return; + } + + for (iteration = 0; iteration <= count; ++iteration) { + const float strength = (iteration != count) ? 1.0f : last; + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .smooth_mask = smooth_mask, + .strength = strength, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + + switch (type) { + case PBVH_GRIDS: { + int gridsize; + size_t size; + SculptDoBrushSmoothGridDataChunk *data_chunk; + + BKE_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL, &gridsize, NULL); + size = (size_t)gridsize; + size = sizeof(float) * size * size * (smooth_mask ? 1 : 3); + data_chunk = MEM_mallocN(sizeof(*data_chunk) + size, __func__); + data_chunk->tmpgrid_size = size; + size += sizeof(*data_chunk); + + settings.userdata_chunk = data_chunk; + settings.userdata_chunk_size = size; + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); + + MEM_freeN(data_chunk); + break; + } + case PBVH_FACES: + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); + break; + case PBVH_BMESH: + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); + break; + } + + if (ss->multires) + multires_stitch_grids(ob); + } } static void bmesh_topology_rake( - Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) { - Brush *brush = BKE_paint_brush(&sd->paint); - CLAMP(bstrength, 0.0f, 1.0f); + Brush *brush = BKE_paint_brush(&sd->paint); + CLAMP(bstrength, 0.0f, 1.0f); - /* Interactions increase both strength and quality. */ - const int iterations = 3; + /* Interactions increase both strength and quality. */ + const int iterations = 3; - int iteration; - const int count = iterations * bstrength + 1; - const float factor = iterations * bstrength / count; + int iteration; + const int count = iterations * bstrength + 1; + const float factor = iterations * bstrength / count; - for (iteration = 0; iteration <= count; ++iteration) { + for (iteration = 0; iteration <= count; ++iteration) { - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .strength = factor, - }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .strength = factor, + }; + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_topology_rake_bmesh_task_cb_ex, - &settings); - } + BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); + } } static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); + SculptSession *ss = ob->sculpt; + smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); } -static void do_mask_brush_draw_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float bstrength = ss->cache->bstrength; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = ss->cache->bstrength; - PBVHVertexIter vd; + PBVHVertexIter vd; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = tex_strength( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, tls->thread_id); - (*vd.mask) += fade * bstrength; - CLAMP(*vd.mask, 0, 1); + (*vd.mask) += fade * bstrength; + CLAMP(*vd.mask, 0, 1); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - BKE_pbvh_vertex_iter_end; - } + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + BKE_pbvh_vertex_iter_end; + } } static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_mask_brush_draw_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); } static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - switch ((BrushMaskTool)brush->mask_tool) { - case BRUSH_MASK_DRAW: - do_mask_brush_draw(sd, ob, nodes, totnode); - break; - case BRUSH_MASK_SMOOTH: - smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); - break; - } + switch ((BrushMaskTool)brush->mask_tool) { + case BRUSH_MASK_DRAW: + do_mask_brush_draw(sd, ob, nodes, totnode); + break; + case BRUSH_MASK_SMOOTH: + smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); + break; + } } -static void do_draw_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_draw_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *offset = data->offset; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *offset = data->offset; - PBVHVertexIter vd; - float (*proxy)[3]; + PBVHVertexIter vd; + float(*proxy)[3]; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* offset vertex */ - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* offset vertex */ + const float fade = tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], offset, fade); + mul_v3_v3fl(proxy[vd.i], offset, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - const float bstrength = ss->cache->bstrength; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; - /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); - /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise - * initialize before threads so they can do curve mapping */ - curvemapping_initialize(brush->curve); + /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise + * initialize before threads so they can do curve mapping */ + curvemapping_initialize(brush->curve); - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, - }; + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_draw_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); } /** * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB' */ -static void do_crease_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - SculptProjectVector *spvc = data->spvc; - const float flippedbstrength = data->flippedbstrength; - const float *offset = data->offset; - - PBVHVertexIter vd; - float (*proxy)[3]; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* offset vertex */ - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float val1[3]; - float val2[3]; - - /* first we pinch */ - sub_v3_v3v3(val1, test.location, vd.co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(val1, val1, ss->cache->view_normal); - } - - mul_v3_fl(val1, fade * flippedbstrength); - - sculpt_project_v3(spvc, val1, val1); - - /* then we draw */ - mul_v3_v3fl(val2, offset, fade); - - add_v3_v3v3(proxy[vd.i], val1, val2); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; +static void do_crease_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + SculptProjectVector *spvc = data->spvc; + const float flippedbstrength = data->flippedbstrength; + const float *offset = data->offset; + + PBVHVertexIter vd; + float(*proxy)[3]; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* offset vertex */ + const float fade = tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float val1[3]; + float val2[3]; + + /* first we pinch */ + sub_v3_v3v3(val1, test.location, vd.co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(val1, val1, ss->cache->view_normal); + } + + mul_v3_fl(val1, fade * flippedbstrength); + + sculpt_project_v3(spvc, val1, val1); + + /* then we draw */ + mul_v3_v3fl(val2, offset, fade); + + add_v3_v3v3(proxy[vd.i], val1, val2); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - const Scene *scene = ss->cache->vc->scene; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - float bstrength = ss->cache->bstrength; - float flippedbstrength, crease_correction; - float brush_alpha; - - SculptProjectVector spvc; - - /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); - - /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ - crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; - brush_alpha = BKE_brush_alpha_get(scene, brush); - if (brush_alpha > 0.0f) - crease_correction /= brush_alpha * brush_alpha; - - /* we always want crease to pinch or blob to relax even when draw is negative */ - flippedbstrength = (bstrength < 0) ? -crease_correction * bstrength : crease_correction * bstrength; - - if (brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; - - /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single point. - * Without this we get a 'flat' surface surrounding the pinch */ - sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); - - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .spvc = &spvc, .offset = offset, .flippedbstrength = flippedbstrength, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_crease_brush_task_cb_ex, - &settings); -} - -static void do_pinch_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float val[3]; - - sub_v3_v3v3(val, test.location, vd.co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(val, val, ss->cache->view_normal); - } - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + const Scene *scene = ss->cache->vc->scene; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + float bstrength = ss->cache->bstrength; + float flippedbstrength, crease_correction; + float brush_alpha; + + SculptProjectVector spvc; + + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ + crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; + brush_alpha = BKE_brush_alpha_get(scene, brush); + if (brush_alpha > 0.0f) + crease_correction /= brush_alpha * brush_alpha; + + /* we always want crease to pinch or blob to relax even when draw is negative */ + flippedbstrength = (bstrength < 0) ? -crease_correction * bstrength : + crease_correction * bstrength; + + if (brush->sculpt_tool == SCULPT_TOOL_BLOB) + flippedbstrength *= -1.0f; + + /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single point. + * Without this we get a 'flat' surface surrounding the pinch */ + sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); + + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .spvc = &spvc, + .offset = offset, + .flippedbstrength = flippedbstrength, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); +} + +static void do_pinch_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float val[3]; + + sub_v3_v3v3(val, test.location, vd.co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(val, val, ss->cache->view_normal); + } + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_pinch_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); } -static void do_grab_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_grab_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *grab_delta = data->grab_delta; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *grab_delta = data->grab_delta; - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - const float fade = bstrength * tex_strength( - ss, brush, orig_data.co, sqrtf(test.dist), - orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .grab_delta = grab_delta, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_grab_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); } -static void do_nudge_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_nudge_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *cono = data->cono; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *cono = data->cono; - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], cono, fade); + mul_v3_v3fl(proxy[vd.i], cono, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; - float tmp[3], cono[3]; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); - cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .cono = cono, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_nudge_brush_task_cb_ex, - &settings); -} - -static void do_snake_hook_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - SculptProjectVector *spvc = data->spvc; - const float *grab_delta = data->grab_delta; - - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; - const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; - const bool do_pinch = (brush->crease_pinch_factor != 0.5f); - const float pinch = do_pinch ? - (2.0f * (0.5f - brush->crease_pinch_factor) * (len_v3(grab_delta) / ss->cache->radius)) : 0.0f; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - - mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - - /* negative pinch will inflate, helps maintain volume */ - if (do_pinch) { - float delta_pinch_init[3], delta_pinch[3]; - - sub_v3_v3v3(delta_pinch, vd.co, test.location); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(delta_pinch, delta_pinch, ss->cache->true_view_normal); - } - - /* important to calculate based on the grabbed location - * (intentionally ignore fade here). */ - add_v3_v3(delta_pinch, grab_delta); - - sculpt_project_v3(spvc, delta_pinch, delta_pinch); - - copy_v3_v3(delta_pinch_init, delta_pinch); - - float pinch_fade = pinch * fade; - /* when reducing, scale reduction back by how close to the center we are, - * so we don't pinch into nothingness */ - if (pinch > 0.0f) { - /* square to have even less impact for close vertices */ - pinch_fade *= pow2f(min_ff(1.0f, len_v3(delta_pinch) / ss->cache->radius)); - } - mul_v3_fl(delta_pinch, 1.0f + pinch_fade); - sub_v3_v3v3(delta_pinch, delta_pinch_init, delta_pinch); - add_v3_v3(proxy[vd.i], delta_pinch); - } - - if (do_rake_rotation) { - float delta_rotate[3]; - sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); - add_v3_v3(proxy[vd.i], delta_rotate); - } - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + float tmp[3], cono[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); + cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cono = cono, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); +} + +static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + SculptProjectVector *spvc = data->spvc; + const float *grab_delta = data->grab_delta; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; + const bool do_pinch = (brush->crease_pinch_factor != 0.5f); + const float pinch = do_pinch ? (2.0f * (0.5f - brush->crease_pinch_factor) * + (len_v3(grab_delta) / ss->cache->radius)) : + 0.0f; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + + /* negative pinch will inflate, helps maintain volume */ + if (do_pinch) { + float delta_pinch_init[3], delta_pinch[3]; + + sub_v3_v3v3(delta_pinch, vd.co, test.location); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(delta_pinch, delta_pinch, ss->cache->true_view_normal); + } + + /* important to calculate based on the grabbed location + * (intentionally ignore fade here). */ + add_v3_v3(delta_pinch, grab_delta); + + sculpt_project_v3(spvc, delta_pinch, delta_pinch); + + copy_v3_v3(delta_pinch_init, delta_pinch); + + float pinch_fade = pinch * fade; + /* when reducing, scale reduction back by how close to the center we are, + * so we don't pinch into nothingness */ + if (pinch > 0.0f) { + /* square to have even less impact for close vertices */ + pinch_fade *= pow2f(min_ff(1.0f, len_v3(delta_pinch) / ss->cache->radius)); + } + mul_v3_fl(delta_pinch, 1.0f + pinch_fade); + sub_v3_v3v3(delta_pinch, delta_pinch_init, delta_pinch); + add_v3_v3(proxy[vd.i], delta_pinch); + } + + if (do_rake_rotation) { + float delta_rotate[3]; + sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); + add_v3_v3(proxy[vd.i], delta_rotate); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - const float bstrength = ss->cache->bstrength; - float grab_delta[3]; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + const float bstrength = ss->cache->bstrength; + float grab_delta[3]; - SculptProjectVector spvc; + SculptProjectVector spvc; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - if (bstrength < 0) - negate_v3(grab_delta); + if (bstrength < 0) + negate_v3(grab_delta); - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } - /* optionally pinch while painting */ - if (brush->crease_pinch_factor != 0.5f) { - sculpt_project_v3_cache_init(&spvc, grab_delta); - } + /* optionally pinch while painting */ + if (brush->crease_pinch_factor != 0.5f) { + sculpt_project_v3_cache_init(&spvc, grab_delta); + } - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .spvc = &spvc, .grab_delta = grab_delta, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .spvc = &spvc, + .grab_delta = grab_delta, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_snake_hook_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); } -static void do_thumb_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_thumb_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *cono = data->cono; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *cono = data->cono; - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - const float fade = bstrength * tex_strength( - ss, brush, orig_data.co, sqrtf(test.dist), - orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], cono, fade); + mul_v3_v3fl(proxy[vd.i], cono, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; - float tmp[3], cono[3]; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + float tmp[3], cono[3]; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); - cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); + cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); + cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .cono = cono, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cono = cono, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_thumb_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); } -static void do_rotate_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_rotate_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float angle = data->angle; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float angle = data->angle; - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - float vec[3], rot[3][3]; - const float fade = bstrength * tex_strength( - ss, brush, orig_data.co, sqrtf(test.dist), - orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + float vec[3], rot[3][3]; + const float fade = bstrength * tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - sub_v3_v3v3(vec, orig_data.co, ss->cache->location); - axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); - mul_v3_m3v3(proxy[vd.i], rot, vec); - add_v3_v3(proxy[vd.i], ss->cache->location); - sub_v3_v3(proxy[vd.i], orig_data.co); + sub_v3_v3v3(vec, orig_data.co, ss->cache->location); + axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); + mul_v3_m3v3(proxy[vd.i], rot, vec); + add_v3_v3(proxy[vd.i], ss->cache->location); + sub_v3_v3(proxy[vd.i], orig_data.co); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; - const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .angle = angle, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_rotate_brush_task_cb_ex, - &settings); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1}; + const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .angle = angle, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); +} + +static void do_layer_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const float *offset = data->offset; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float *layer_disp; + const float bstrength = ss->cache->bstrength; + const float lim = (bstrength < 0) ? -data->brush->height : data->brush->height; + /* XXX: layer brush needs conversion to proxy but its more complicated */ + /* proxy = BKE_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */ + + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + /* Why does this have to be thread-protected? */ + BLI_mutex_lock(&data->mutex); + layer_disp = BKE_pbvh_node_layer_disp_get(ss->pbvh, data->nodes[n]); + BLI_mutex_unlock(&data->mutex); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float *disp = &layer_disp[vd.i]; + float val[3]; + + *disp += fade; + + /* Don't let the displacement go past the limit */ + if ((lim < 0.0f && *disp < lim) || (lim >= 0.0f && *disp > lim)) + *disp = lim; + + mul_v3_v3fl(val, offset, *disp); + + if (!ss->multires && !ss->bm && ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + int index = vd.vert_indices[vd.i]; + + /* persistent base */ + add_v3_v3(val, ss->layer_co[index]); + } + else { + add_v3_v3(val, orig_data.co); + } + + sculpt_clip(sd, ss, vd.co, val); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } -static void do_layer_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const float *offset = data->offset; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float *layer_disp; - const float bstrength = ss->cache->bstrength; - const float lim = (bstrength < 0) ? -data->brush->height : data->brush->height; - /* XXX: layer brush needs conversion to proxy but its more complicated */ - /* proxy = BKE_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm); - /* Why does this have to be thread-protected? */ - BLI_mutex_lock(&data->mutex); - layer_disp = BKE_pbvh_node_layer_disp_get(ss->pbvh, data->nodes[n]); - BLI_mutex_unlock(&data->mutex); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; + BLI_mutex_init(&data.mutex); - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BLI_mutex_end(&data.mutex); +} - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float *disp = &layer_disp[vd.i]; - float val[3]; +static void do_inflate_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; - *disp += fade; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - /* Don't let the displacement go past the limit */ - if ((lim < 0.0f && *disp < lim) || (lim >= 0.0f && *disp > lim)) - *disp = lim; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - mul_v3_v3fl(val, offset, *disp); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - if (!ss->multires && !ss->bm && ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { - int index = vd.vert_indices[vd.i]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float val[3]; - /* persistent base */ - add_v3_v3(val, ss->layer_co[index]); - } - else { - add_v3_v3(val, orig_data.co); - } + if (vd.fno) + copy_v3_v3(val, vd.fno); + else + normal_short_to_float_v3(val, vd.no); - sculpt_clip(sd, ss, vd.co, val); + mul_v3_fl(val, fade * ss->cache->radius); + mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } -static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - - mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, - }; - BLI_mutex_init(&data.mutex); + Brush *brush = BKE_paint_brush(&sd->paint); - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_layer_brush_task_cb_ex, - &settings); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - BLI_mutex_end(&data.mutex); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); } -static void do_inflate_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void calc_sculpt_plane( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && + ss->cache->tile_pass == 0 && + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + copy_v3_v3(r_area_no, ss->cache->true_view_normal); + break; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + case SCULPT_DISP_DIR_X: + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); + break; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + case SCULPT_DISP_DIR_Y: + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); + break; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float val[3]; + case SCULPT_DISP_DIR_Z: + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); + break; - if (vd.fno) - copy_v3_v3(val, vd.fno); - else - normal_short_to_float_v3(val, vd.no); + case SCULPT_DISP_DIR_AREA: + calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); + normalize_v3(r_area_no); + } + break; - mul_v3_fl(val, fade * ss->cache->radius); - mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); + default: + break; + } - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} + /* for flatten center */ + /* flatten center has not been calculated yet if we are not using the area normal */ + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) + calc_area_center(sd, ob, nodes, totnode, r_area_co); -static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - Brush *brush = BKE_paint_brush(&sd->paint); + /* for area normal */ + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* for flatten center */ + copy_v3_v3(ss->cache->last_center, r_area_co); + } + else { + /* for area normal */ + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_inflate_brush_task_cb_ex, - &settings); -} + /* for flatten center */ + copy_v3_v3(r_area_co, ss->cache->last_center); -static void calc_sculpt_plane( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3], float r_area_co[3]) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (ss->cache->mirror_symmetry_pass == 0 && - ss->cache->radial_symmetry_pass == 0 && - ss->cache->tile_pass == 0 && - (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) - { - switch (brush->sculpt_plane) { - case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(r_area_no, ss->cache->true_view_normal); - break; - - case SCULPT_DISP_DIR_X: - ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); - break; - - case SCULPT_DISP_DIR_Y: - ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); - break; - - case SCULPT_DISP_DIR_Z: - ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); - break; - - case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); - normalize_v3(r_area_no); - } - break; - - default: - break; - } - - /* for flatten center */ - /* flatten center has not been calculated yet if we are not using the area normal */ - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) - calc_area_center(sd, ob, nodes, totnode, r_area_co); - - /* for area normal */ - copy_v3_v3(ss->cache->sculpt_normal, r_area_no); - - /* for flatten center */ - copy_v3_v3(ss->cache->last_center, r_area_co); - } - else { - /* for area normal */ - copy_v3_v3(r_area_no, ss->cache->sculpt_normal); - - /* for flatten center */ - copy_v3_v3(r_area_co, ss->cache->last_center); - - /* for area normal */ - flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); - - /* for flatten center */ - flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); - - /* for area normal */ - mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); - - /* for flatten center */ - mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); - - /* shift the plane for the current tile */ - add_v3_v3(r_area_co, ss->cache->plane_offset); - } + /* for area normal */ + flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); + + /* for flatten center */ + flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); + + /* for area normal */ + mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); + + /* for flatten center */ + mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); + + /* shift the plane for the current tile */ + add_v3_v3(r_area_co, ss->cache->plane_offset); + } } static int plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { - return (!(brush->flag & BRUSH_PLANE_TRIM) || - ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); + return (!(brush->flag & BRUSH_PLANE_TRIM) || + ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); } -static bool plane_point_side_flip( - const float co[3], const float plane[4], - const bool flip) +static bool plane_point_side_flip(const float co[3], const float plane[4], const bool flip) { - float d = plane_point_side_v3(plane, co); - if (flip) d = -d; - return d <= 0.0f; + float d = plane_point_side_v3(plane, co); + if (flip) + d = -d; + return d <= 0.0f; } static int plane_point_side(const float co[3], const float plane[4]) { - float d = plane_point_side_v3(plane, co); - return d <= 0.0f; + float d = plane_point_side_v3(plane, co); + return d <= 0.0f; } static float get_offset(Sculpt *sd, SculptSession *ss) { - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); - float rv = brush->plane_offset; + float rv = brush->plane_offset; - if (brush->flag & BRUSH_OFFSET_PRESSURE) { - rv *= ss->cache->pressure; - } + if (brush->flag & BRUSH_OFFSET_PRESSURE) { + rv *= ss->cache->pressure; + } - return rv; + return rv; } -static void do_flatten_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_flatten_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const float radius = ss->cache->radius; + const float radius = ss->cache->radius; - float area_no[3]; - float area_co[3]; + float area_no[3]; + float area_co[3]; - float offset = get_offset(sd, ss); - float displace; - float temp[3]; + float offset = get_offset(sd, ss); + float displace; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = radius * offset; + displace = radius * offset; - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_flatten_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); } -static void do_clay_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_clay_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; - PBVHVertexIter vd; - float (*proxy)[3]; - const bool flip = (ss->cache->bstrength < 0); - const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const bool flip = (ss->cache->bstrength < 0); + const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - /* note, the normal from the vertices is ignored, - * causes glitch with planes, see: T44390 */ - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const bool flip = (ss->cache->bstrength < 0); - const float radius = flip ? -ss->cache->radius : ss->cache->radius; + const bool flip = (ss->cache->bstrength < 0); + const float radius = flip ? -ss->cache->radius : ss->cache->radius; - float offset = get_offset(sd, ss); - float displace; + float offset = get_offset(sd, ss); + float displace; - float area_no[3]; - float area_co[3]; - float temp[3]; + float area_no[3]; + float area_co[3]; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = radius * (0.25f + offset); + displace = radius * (0.25f + offset); - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - /* add_v3_v3v3(p, ss->cache->location, area_no); */ + /* add_v3_v3v3(p, ss->cache->location, area_no); */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_clay_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); } -static void do_clay_strips_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float (*mat)[4] = data->mat; - const float *area_no_sp = data->area_no_sp; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float(*mat)[4] = data->mat; + const float *area_no_sp = data->area_no_sp; + const float *area_co = data->area_co; - PBVHVertexIter vd; - SculptBrushTest test; - float (*proxy)[3]; - const bool flip = (ss->cache->bstrength < 0); - const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; + PBVHVertexIter vd; + SculptBrushTest test; + float(*proxy)[3]; + const bool flip = (ss->cache->bstrength < 0); + const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - sculpt_brush_test_init(ss, &test); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); + sculpt_brush_test_init(ss, &test); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_cube(&test, vd.co, mat)) { - if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_cube(&test, vd.co, mat)) { + if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - /* note, the normal from the vertices is ignored, - * causes glitch with planes, see: T44390 */ - const float fade = bstrength * tex_strength( - ss, brush, vd.co, ss->cache->radius * test.dist, - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + ss->cache->radius * test.dist, + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const bool flip = (ss->cache->bstrength < 0); - const float radius = flip ? -ss->cache->radius : ss->cache->radius; - const float offset = get_offset(sd, ss); - const float displace = radius * (0.25f + offset); - - float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ - float area_no[3]; /* geometry normal */ - float area_co[3]; - - float temp[3]; - float mat[4][4]; - float scale[4][4]; - float tmat[4][4]; - - calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co); - - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) - calc_area_normal(sd, ob, nodes, totnode, area_no); - else - copy_v3_v3(area_no, area_no_sp); - - /* delay the first daub because grab delta is not setup */ - if (ss->cache->first_time) - return; - - mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - /* init mat */ - cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); - mat[0][3] = 0; - cross_v3_v3v3(mat[1], area_no, mat[0]); - mat[1][3] = 0; - copy_v3_v3(mat[2], area_no); - mat[2][3] = 0; - copy_v3_v3(mat[3], ss->cache->location); - mat[3][3] = 1; - normalize_m4(mat); - - /* scale mat */ - scale_m4_fl(scale, ss->cache->radius); - mul_m4_m4m4(tmat, mat, scale); - invert_m4_m4(mat, tmat); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no_sp = area_no_sp, .area_co = area_co, .mat = mat, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_clay_strips_brush_task_cb_ex, - &settings); -} - -static void do_fill_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - if (plane_point_side(vd.co, test.plane_tool)) { - float intr[3]; - float val[3]; - - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - - sub_v3_v3v3(val, intr, vd.co); - - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const bool flip = (ss->cache->bstrength < 0); + const float radius = flip ? -ss->cache->radius : ss->cache->radius; + const float offset = get_offset(sd, ss); + const float displace = radius * (0.25f + offset); + + float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ + float area_no[3]; /* geometry normal */ + float area_co[3]; + + float temp[3]; + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) + calc_area_normal(sd, ob, nodes, totnode, area_no); + else + copy_v3_v3(area_no, area_no_sp); + + /* delay the first daub because grab delta is not setup */ + if (ss->cache->first_time) + return; + + mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + /* init mat */ + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); + mat[0][3] = 0; + cross_v3_v3v3(mat[1], area_no, mat[0]); + mat[1][3] = 0; + copy_v3_v3(mat[2], area_no); + mat[2][3] = 0; + copy_v3_v3(mat[3], ss->cache->location); + mat[3][3] = 1; + normalize_m4(mat); + + /* scale mat */ + scale_m4_fl(scale, ss->cache->radius); + mul_m4_m4m4(tmat, mat, scale); + invert_m4_m4(mat, tmat); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no_sp = area_no_sp, + .area_co = area_co, + .mat = mat, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); +} + +static void do_fill_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + if (plane_point_side(vd.co, test.plane_tool)) { + float intr[3]; + float val[3]; + + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const float radius = ss->cache->radius; + const float radius = ss->cache->radius; - float area_no[3]; - float area_co[3]; - float offset = get_offset(sd, ss); + float area_no[3]; + float area_co[3]; + float offset = get_offset(sd, ss); - float displace; + float displace; - float temp[3]; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = radius * offset; + displace = radius * offset; - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_fill_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); } -static void do_scrape_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_scrape_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - if (!plane_point_side(vd.co, test.plane_tool)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + if (!plane_point_side(vd.co, test.plane_tool)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const float radius = ss->cache->radius; + const float radius = ss->cache->radius; - float area_no[3]; - float area_co[3]; - float offset = get_offset(sd, ss); + float area_no[3]; + float area_co[3]; + float offset = get_offset(sd, ss); - float displace; + float displace; - float temp[3]; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = -radius * offset; + displace = -radius * offset; - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_scrape_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); } -static void do_gravity_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_gravity_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float *offset = data->offset; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float *offset = data->offset; - PBVHVertexIter vd; - float (*proxy)[3]; + PBVHVertexIter vd; + float(*proxy)[3]; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], offset, fade); + mul_v3_v3fl(proxy[vd.i], offset, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]/*, area_no[3]*/; - float gravity_vector[3]; + float offset[3] /*, area_no[3]*/; + float gravity_vector[3]; - mul_v3_v3fl(gravity_vector, ss->cache->gravity_direction, -ss->cache->radius_squared); + mul_v3_v3fl(gravity_vector, ss->cache->gravity_direction, -ss->cache->radius_squared); - /* offset with as much as possible factored in already */ - mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); - mul_v3_fl(offset, bstrength); + /* offset with as much as possible factored in already */ + mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); + mul_v3_fl(offset, bstrength); - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, - }; + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_gravity_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); } - void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) { - Mesh *me = (Mesh *)ob->data; - float (*ofs)[3] = NULL; - int a; - const int kb_act_idx = ob->shapenr - 1; - KeyBlock *currkey; + Mesh *me = (Mesh *)ob->data; + float(*ofs)[3] = NULL; + int a; + const int kb_act_idx = ob->shapenr - 1; + KeyBlock *currkey; - /* for relative keys editing of base should update other keys */ - if (BKE_keyblock_is_basis(me->key, kb_act_idx)) { - ofs = BKE_keyblock_convert_to_vertcos(ob, kb); + /* for relative keys editing of base should update other keys */ + if (BKE_keyblock_is_basis(me->key, kb_act_idx)) { + ofs = BKE_keyblock_convert_to_vertcos(ob, kb); - /* calculate key coord offsets (from previous location) */ - for (a = 0; a < me->totvert; a++) { - sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]); - } + /* calculate key coord offsets (from previous location) */ + for (a = 0; a < me->totvert; a++) { + sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]); + } - /* apply offsets on other keys */ - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { - if ((currkey != kb) && (currkey->relative == kb_act_idx)) { - BKE_keyblock_update_from_offset(ob, currkey, ofs); - } - } + /* apply offsets on other keys */ + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if ((currkey != kb) && (currkey->relative == kb_act_idx)) { + BKE_keyblock_update_from_offset(ob, currkey, ofs); + } + } - MEM_freeN(ofs); - } + MEM_freeN(ofs); + } - /* modifying of basis key should update mesh */ - if (kb == me->key->refkey) { - MVert *mvert = me->mvert; + /* modifying of basis key should update mesh */ + if (kb == me->key->refkey) { + MVert *mvert = me->mvert; - for (a = 0; a < me->totvert; a++, mvert++) - copy_v3_v3(mvert->co, vertCos[a]); + for (a = 0; a < me->totvert; a++, mvert++) + copy_v3_v3(mvert->co, vertCos[a]); - BKE_mesh_calc_normals(me); - } + BKE_mesh_calc_normals(me); + } - /* apply new coords on active key block, no need to re-allocate kb->data here! */ - BKE_keyblock_update_from_vertcos(ob, kb, vertCos); + /* apply new coords on active key block, no need to re-allocate kb->data here! */ + BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } /* Note: we do the topology update before any brush actions to avoid * issues with the proxies. The size of the proxy can't change, so * topology must be updated first. */ -static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *UNUSED(ups)) -{ - SculptSession *ss = ob->sculpt; - - int n, totnode; - /* Build a list of all nodes that are potentially within the brush's area of influence */ - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; - const float radius_scale = 1.25f; - PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); - - /* Only act if some verts are inside the brush area */ - if (totnode) { - PBVHTopologyUpdateMode mode = 0; - float location[3]; - - if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { - if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { - mode |= PBVH_Subdivide; - } - - if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || - (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) - { - mode |= PBVH_Collapse; - } - } - - for (n = 0; n < totnode; n++) { - sculpt_undo_push_node(ob, nodes[n], - brush->sculpt_tool == SCULPT_TOOL_MASK ? - SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(nodes[n]); - - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_node_mark_topology_update(nodes[n]); - BKE_pbvh_bmesh_node_save_orig(nodes[n]); - } - } - - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_update_topology( - ss->pbvh, mode, - ss->cache->location, - ss->cache->view_normal, - ss->cache->radius, - (brush->flag & BRUSH_FRONTFACE) != 0, - (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); - } - - MEM_freeN(nodes); - - /* update average stroke position */ - copy_v3_v3(location, ss->cache->true_location); - mul_m4_v3(ob->obmat, location); - } -} - -static void do_brush_action_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - - sculpt_undo_push_node(data->ob, data->nodes[n], - data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(data->nodes[n]); +static void sculpt_topology_update(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *UNUSED(ups)) +{ + SculptSession *ss = ob->sculpt; + + int n, totnode; + /* Build a list of all nodes that are potentially within the brush's area of influence */ + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + const float radius_scale = 1.25f; + PBVHNode **nodes = sculpt_pbvh_gather_generic( + ob, sd, brush, use_original, radius_scale, &totnode); + + /* Only act if some verts are inside the brush area */ + if (totnode) { + PBVHTopologyUpdateMode mode = 0; + float location[3]; + + if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { + if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + mode |= PBVH_Subdivide; + } + + if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) { + mode |= PBVH_Collapse; + } + } + + for (n = 0; n < totnode; n++) { + sculpt_undo_push_node(ob, + nodes[n], + brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); + BKE_pbvh_node_mark_update(nodes[n]); + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_node_mark_topology_update(nodes[n]); + BKE_pbvh_bmesh_node_save_orig(nodes[n]); + } + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_bmesh_update_topology(ss->pbvh, + mode, + ss->cache->location, + ss->cache->view_normal, + ss->cache->radius, + (brush->flag & BRUSH_FRONTFACE) != 0, + (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); + } + + MEM_freeN(nodes); + + /* update average stroke position */ + copy_v3_v3(location, ss->cache->true_location); + mul_m4_v3(ob->obmat, location); + } +} + +static void do_brush_action_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + + sculpt_undo_push_node(data->ob, + data->nodes[n], + data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); + BKE_pbvh_node_mark_update(data->nodes[n]); } static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) { - SculptSession *ss = ob->sculpt; - int totnode; - - /* Build a list of all nodes that are potentially within the brush's area of influence */ - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; - const float radius_scale = 1.0f; - PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); - - /* Only act if some verts are inside the brush area */ - if (totnode) { - float location[3]; - - SculptThreadedTaskData task_data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &task_data, - do_brush_action_task_cb, - &settings); - - if (sculpt_brush_needs_normal(ss, brush)) - update_sculpt_normal(sd, ob, nodes, totnode); - - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) - update_brush_local_mat(sd, ob); - - /* Apply one type of brush action */ - switch (brush->sculpt_tool) { - case SCULPT_TOOL_DRAW: - do_draw_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_SMOOTH: - do_smooth_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_CREASE: - do_crease_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_BLOB: - do_crease_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_PINCH: - do_pinch_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_INFLATE: - do_inflate_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_GRAB: - do_grab_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_ROTATE: - do_rotate_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_SNAKE_HOOK: - do_snake_hook_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_NUDGE: - do_nudge_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_THUMB: - do_thumb_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_LAYER: - do_layer_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_FLATTEN: - do_flatten_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_CLAY: - do_clay_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_CLAY_STRIPS: - do_clay_strips_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_FILL: - do_fill_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_SCRAPE: - do_scrape_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_MASK: - do_mask_brush(sd, ob, nodes, totnode); - break; - } - - if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && - brush->autosmooth_factor > 0) - { - if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { - smooth(sd, ob, nodes, totnode, brush->autosmooth_factor * (1 - ss->cache->pressure), false); - } - else { - smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false); - } - } - - if (sculpt_brush_use_topology_rake(ss, brush)) { - bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); - } - - if (ss->cache->supports_gravity) - do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); - - MEM_freeN(nodes); - - /* update average stroke position */ - copy_v3_v3(location, ss->cache->true_location); - mul_m4_v3(ob->obmat, location); - - add_v3_v3(ups->average_stroke_accum, location); - ups->average_stroke_counter++; - /* update last stroke position */ - ups->last_stroke_valid = true; - } + SculptSession *ss = ob->sculpt; + int totnode; + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + const float radius_scale = 1.0f; + PBVHNode **nodes = sculpt_pbvh_gather_generic( + ob, sd, brush, use_original, radius_scale, &totnode); + + /* Only act if some verts are inside the brush area */ + if (totnode) { + float location[3]; + + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + + if (sculpt_brush_needs_normal(ss, brush)) + update_sculpt_normal(sd, ob, nodes, totnode); + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) + update_brush_local_mat(sd, ob); + + /* Apply one type of brush action */ + switch (brush->sculpt_tool) { + case SCULPT_TOOL_DRAW: + do_draw_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SMOOTH: + do_smooth_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CREASE: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_BLOB: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_PINCH: + do_pinch_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_INFLATE: + do_inflate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_GRAB: + do_grab_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_ROTATE: + do_rotate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SNAKE_HOOK: + do_snake_hook_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_NUDGE: + do_nudge_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_THUMB: + do_thumb_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_LAYER: + do_layer_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FLATTEN: + do_flatten_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY: + do_clay_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY_STRIPS: + do_clay_strips_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FILL: + do_fill_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SCRAPE: + do_scrape_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_MASK: + do_mask_brush(sd, ob, nodes, totnode); + break; + } + + if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && + brush->autosmooth_factor > 0) { + if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { + smooth( + sd, ob, nodes, totnode, brush->autosmooth_factor * (1 - ss->cache->pressure), false); + } + else { + smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false); + } + } + + if (sculpt_brush_use_topology_rake(ss, brush)) { + bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); + } + + if (ss->cache->supports_gravity) + do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); + + MEM_freeN(nodes); + + /* update average stroke position */ + copy_v3_v3(location, ss->cache->true_location); + mul_m4_v3(ob->obmat, location); + + add_v3_v3(ups->average_stroke_accum, location); + ups->average_stroke_counter++; + /* update last stroke position */ + ups->last_stroke_valid = true; + } } /* flush displacement from deformed PBVH vertex to original mesh */ static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) { - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - float disp[3], newco[3]; - int index = vd->vert_indices[vd->i]; + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + float disp[3], newco[3]; + int index = vd->vert_indices[vd->i]; - sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]); - mul_m3_v3(ss->deform_imats[index], disp); - add_v3_v3v3(newco, disp, ss->orig_cos[index]); + sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]); + mul_m3_v3(ss->deform_imats[index], disp); + add_v3_v3v3(newco, disp, ss->orig_cos[index]); - copy_v3_v3(ss->deform_cos[index], vd->co); - copy_v3_v3(ss->orig_cos[index], newco); + copy_v3_v3(ss->deform_cos[index], vd->co); + copy_v3_v3(ss->orig_cos[index], newco); - if (!ss->kb) - copy_v3_v3(me->mvert[index].co, newco); + if (!ss->kb) + copy_v3_v3(me->mvert[index].co, newco); } -static void sculpt_combine_proxies_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void sculpt_combine_proxies_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - Object *ob = data->ob; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + Object *ob = data->ob; - /* these brushes start from original coordinates */ - const bool use_orco = ELEM(data->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); + /* these brushes start from original coordinates */ + const bool use_orco = ELEM( + data->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); - PBVHVertexIter vd; - PBVHProxyNode *proxies; - int proxy_count; - float (*orco)[3] = NULL; + PBVHVertexIter vd; + PBVHProxyNode *proxies; + int proxy_count; + float(*orco)[3] = NULL; - if (use_orco && !ss->bm) - orco = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co; + if (use_orco && !ss->bm) + orco = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co; - BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); + BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - float val[3]; - int p; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + float val[3]; + int p; - if (use_orco) { - if (ss->bm) { - copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); - } - else { - copy_v3_v3(val, orco[vd.i]); - } - } - else { - copy_v3_v3(val, vd.co); - } + if (use_orco) { + if (ss->bm) { + copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); + } + else { + copy_v3_v3(val, orco[vd.i]); + } + } + else { + copy_v3_v3(val, vd.co); + } - for (p = 0; p < proxy_count; p++) - add_v3_v3(val, proxies[p].co[vd.i]); + for (p = 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); - sculpt_clip(sd, ss, vd.co, val); + sculpt_clip(sd, ss, vd.co, val); - if (ss->modifiers_active) - sculpt_flush_pbvhvert_deform(ob, &vd); - } - BKE_pbvh_vertex_iter_end; + if (ss->modifiers_active) + sculpt_flush_pbvhvert_deform(ob, &vd); + } + BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_free_proxies(data->nodes[n]); + BKE_pbvh_node_free_proxies(data->nodes[n]); } static void sculpt_combine_proxies(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - PBVHNode **nodes; - int totnode; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + PBVHNode **nodes; + int totnode; - BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); - /* first line is tools that don't support proxies */ - if (ss->cache->supports_gravity || - (sculpt_tool_is_proxy_used(brush->sculpt_tool) == false)) - { - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* first line is tools that don't support proxies */ + if (ss->cache->supports_gravity || (sculpt_tool_is_proxy_used(brush->sculpt_tool) == false)) { + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - sculpt_combine_proxies_task_cb, - &settings); - } + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + } - if (nodes) - MEM_freeN(nodes); + if (nodes) + MEM_freeN(nodes); } /* copy the modified vertices from bvh to the active key */ static void sculpt_update_keyblock(Object *ob) { - SculptSession *ss = ob->sculpt; - float (*vertCos)[3]; + SculptSession *ss = ob->sculpt; + float(*vertCos)[3]; - /* Keyblock update happens after handling deformation caused by modifiers, - * so ss->orig_cos would be updated with new stroke */ - if (ss->orig_cos) vertCos = ss->orig_cos; - else vertCos = BKE_pbvh_get_vertCos(ss->pbvh); + /* Keyblock update happens after handling deformation caused by modifiers, + * so ss->orig_cos would be updated with new stroke */ + if (ss->orig_cos) + vertCos = ss->orig_cos; + else + vertCos = BKE_pbvh_get_vertCos(ss->pbvh); - if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + if (vertCos) { + sculpt_vertcos_to_key(ob, ss->kb, vertCos); - if (vertCos != ss->orig_cos) - MEM_freeN(vertCos); - } + if (vertCos != ss->orig_cos) + MEM_freeN(vertCos); + } } -static void sculpt_flush_stroke_deform_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void sculpt_flush_stroke_deform_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Object *ob = data->ob; - float (*vertCos)[3] = data->vertCos; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Object *ob = data->ob; + float(*vertCos)[3] = data->vertCos; - PBVHVertexIter vd; + PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_flush_pbvhvert_deform(ob, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_flush_pbvhvert_deform(ob, &vd); - if (vertCos) { - int index = vd.vert_indices[vd.i]; - copy_v3_v3(vertCos[index], ss->orig_cos[index]); - } - } - BKE_pbvh_vertex_iter_end; + if (vertCos) { + int index = vd.vert_indices[vd.i]; + copy_v3_v3(vertCos[index], ss->orig_cos[index]); + } + } + BKE_pbvh_vertex_iter_end; } /* flush displacement from deformed PBVH to original layer */ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { - /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't - * propagate needed deformation to original base */ - - int totnode; - Mesh *me = (Mesh *)ob->data; - PBVHNode **nodes; - float (*vertCos)[3] = NULL; - - if (ss->kb) { - vertCos = MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts"); - - /* mesh could have isolated verts which wouldn't be in BVH, - * to deal with this we copy old coordinates over new ones - * and then update coordinates for all vertices from BVH - */ - memcpy(vertCos, ss->orig_cos, sizeof(*vertCos) * me->totvert); - } - - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .vertCos = vertCos, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - sculpt_flush_stroke_deform_task_cb, - &settings); - - if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); - MEM_freeN(vertCos); - } - - MEM_freeN(nodes); - - /* Modifiers could depend on mesh normals, so we should update them/ - * Note, then if sculpting happens on locked key, normals should be re-calculated - * after applying coords from keyblock on base mesh */ - BKE_mesh_calc_normals(me); - } - else if (ss->kb) { - sculpt_update_keyblock(ob); - } + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { + /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't + * propagate needed deformation to original base */ + + int totnode; + Mesh *me = (Mesh *)ob->data; + PBVHNode **nodes; + float(*vertCos)[3] = NULL; + + if (ss->kb) { + vertCos = MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts"); + + /* mesh could have isolated verts which wouldn't be in BVH, + * to deal with this we copy old coordinates over new ones + * and then update coordinates for all vertices from BVH + */ + memcpy(vertCos, ss->orig_cos, sizeof(*vertCos) * me->totvert); + } + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .vertCos = vertCos, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, sculpt_flush_stroke_deform_task_cb, &settings); + + if (vertCos) { + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + MEM_freeN(vertCos); + } + + MEM_freeN(nodes); + + /* Modifiers could depend on mesh normals, so we should update them/ + * Note, then if sculpting happens on locked key, normals should be re-calculated + * after applying coords from keyblock on base mesh */ + BKE_mesh_calc_normals(me); + } + else if (ss->kb) { + sculpt_update_keyblock(ob); + } } /* Flip all the editdata across the axis/axes specified by symm. Used to * calculate multiple modifications to the mesh when symmetry is enabled. */ -void sculpt_cache_calc_brushdata_symm( - StrokeCache *cache, const char symm, - const char axis, const float angle) -{ - flip_v3_v3(cache->location, cache->true_location, symm); - flip_v3_v3(cache->last_location, cache->true_last_location, symm); - flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm); - flip_v3_v3(cache->view_normal, cache->true_view_normal, symm); - - /* XXX This reduces the length of the grab delta if it approaches the line of symmetry - * XXX However, a different approach appears to be needed */ +void sculpt_cache_calc_brushdata_symm(StrokeCache *cache, + const char symm, + const char axis, + const float angle) +{ + flip_v3_v3(cache->location, cache->true_location, symm); + flip_v3_v3(cache->last_location, cache->true_last_location, symm); + flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm); + flip_v3_v3(cache->view_normal, cache->true_view_normal, symm); + + /* XXX This reduces the length of the grab delta if it approaches the line of symmetry + * XXX However, a different approach appears to be needed */ #if 0 - if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { - float frac = 1.0f / max_overlap_count(sd); - float reduce = (feather - frac) / (1 - frac); + if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { + float frac = 1.0f / max_overlap_count(sd); + float reduce = (feather - frac) / (1 - frac); - printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); + printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); - if (frac < 1) - mul_v3_fl(cache->grab_delta_symmetry, reduce); - } + if (frac < 1) + mul_v3_fl(cache->grab_delta_symmetry, reduce); + } #endif - unit_m4(cache->symm_rot_mat); - unit_m4(cache->symm_rot_mat_inv); - zero_v3(cache->plane_offset); + unit_m4(cache->symm_rot_mat); + unit_m4(cache->symm_rot_mat_inv); + zero_v3(cache->plane_offset); - if (axis) { /* expects XYZ */ - rotate_m4(cache->symm_rot_mat, axis, angle); - rotate_m4(cache->symm_rot_mat_inv, axis, -angle); - } + if (axis) { /* expects XYZ */ + rotate_m4(cache->symm_rot_mat, axis, angle); + rotate_m4(cache->symm_rot_mat_inv, axis, -angle); + } - mul_m4_v3(cache->symm_rot_mat, cache->location); - mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); + mul_m4_v3(cache->symm_rot_mat, cache->location); + mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); - if (cache->supports_gravity) { - flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm); - mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); - } + if (cache->supports_gravity) { + flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm); + mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); + } - if (cache->is_rake_rotation_valid) { - flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); - } + if (cache->is_rake_rotation_valid) { + flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); + } } typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups); -static void do_tiled(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action) -{ - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const float radius = cache->radius; - BoundBox *bb = BKE_object_boundbox_get(ob); - const float *bbMin = bb->vec[0]; - const float *bbMax = bb->vec[6]; - const float *step = sd->paint.tile_offset; - int dim; - - /* These are integer locations, for real location: multiply with step and add orgLoc. - * So 0,0,0 is at orgLoc. */ - int start[3]; - int end[3]; - int cur[3]; - - float orgLoc[3]; /* position of the "prototype" stroke for tiling */ - copy_v3_v3(orgLoc, cache->location); - - for (dim = 0; dim < 3; ++dim) { - if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { - start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; - end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; - } - else - start[dim] = end[dim] = 0; - } - - /* first do the "untiled" position to initialize the stroke for this location */ - cache->tile_pass = 0; - action(sd, ob, brush, ups); - - /* now do it for all the tiles */ - copy_v3_v3_int(cur, start); - for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) { - for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) { - for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) { - if (!cur[0] && !cur[1] && !cur[2]) - continue; /* skip tile at orgLoc, this was already handled before all others */ - - ++cache->tile_pass; - - for (dim = 0; dim < 3; ++dim) { - cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim]; - cache->plane_offset[dim] = cur[dim] * step[dim]; - } - action(sd, ob, brush, ups); - } - } - } -} - - -static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, +static void do_tiled( + Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action) +{ + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const float radius = cache->radius; + BoundBox *bb = BKE_object_boundbox_get(ob); + const float *bbMin = bb->vec[0]; + const float *bbMax = bb->vec[6]; + const float *step = sd->paint.tile_offset; + int dim; + + /* These are integer locations, for real location: multiply with step and add orgLoc. + * So 0,0,0 is at orgLoc. */ + int start[3]; + int end[3]; + int cur[3]; + + float orgLoc[3]; /* position of the "prototype" stroke for tiling */ + copy_v3_v3(orgLoc, cache->location); + + for (dim = 0; dim < 3; ++dim) { + if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { + start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; + end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; + } + else + start[dim] = end[dim] = 0; + } + + /* first do the "untiled" position to initialize the stroke for this location */ + cache->tile_pass = 0; + action(sd, ob, brush, ups); + + /* now do it for all the tiles */ + copy_v3_v3_int(cur, start); + for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) { + for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) { + for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) { + if (!cur[0] && !cur[1] && !cur[2]) + continue; /* skip tile at orgLoc, this was already handled before all others */ + + ++cache->tile_pass; + + for (dim = 0; dim < 3; ++dim) { + cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim]; + cache->plane_offset[dim] = cur[dim] * step[dim]; + } + action(sd, ob, brush, ups); + } + } + } +} + +static void do_radial_symmetry(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *ups, BrushActionFunc action, - const char symm, const int axis, + const char symm, + const int axis, const float UNUSED(feather)) { - SculptSession *ss = ob->sculpt; - int i; + SculptSession *ss = ob->sculpt; + int i; - for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { - const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; - ss->cache->radial_symmetry_pass = i; - sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - do_tiled(sd, ob, brush, ups, action); - } + for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { + const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; + ss->cache->radial_symmetry_pass = i; + sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); + do_tiled(sd, ob, brush, ups, action); + } } /* noise texture gives different values for the same input coord; this @@ -4205,140 +4244,137 @@ static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, UnifiedPain * case */ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - MTex *mtex = &brush->mtex; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + MTex *mtex = &brush->mtex; - if (ss->multires && mtex->tex && mtex->tex->type == TEX_NOISE) - multires_stitch_grids(ob); + if (ss->multires && mtex->tex && mtex->tex->type == TEX_NOISE) + multires_stitch_grids(ob); } -static void do_symmetrical_brush_actions( - Sculpt *sd, Object *ob, - BrushActionFunc action, UnifiedPaintSettings *ups) +static void do_symmetrical_brush_actions(Sculpt *sd, + Object *ob, + BrushActionFunc action, + UnifiedPaintSettings *ups) { - Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - int i; + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + int i; - float feather = calc_symmetry_feather(sd, ss->cache); + float feather = calc_symmetry_feather(sd, ss->cache); - cache->bstrength = brush_strength(sd, cache, feather, ups); - cache->symmetry = symm; + cache->bstrength = brush_strength(sd, cache, feather, ups); + cache->symmetry = symm; - /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 0; i <= symm; ++i) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - cache->mirror_symmetry_pass = i; - cache->radial_symmetry_pass = 0; + /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for (i = 0; i <= symm; ++i) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + cache->mirror_symmetry_pass = i; + cache->radial_symmetry_pass = 0; - sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); - do_tiled(sd, ob, brush, ups, action); + sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); + do_tiled(sd, ob, brush, ups, action); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'X', feather); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'Y', feather); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'Z', feather); - } - } + do_radial_symmetry(sd, ob, brush, ups, action, i, 'X', feather); + do_radial_symmetry(sd, ob, brush, ups, action, i, 'Y', feather); + do_radial_symmetry(sd, ob, brush, ups, action, i, 'Z', feather); + } + } } 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); + 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; - } + if (ss->texcache) { + MEM_freeN(ss->texcache); + ss->texcache = NULL; + } - if (ss->tex_pool) { - BKE_image_pool_free(ss->tex_pool); - ss->tex_pool = NULL; - } + if (ss->tex_pool) { + BKE_image_pool_free(ss->tex_pool); + ss->tex_pool = NULL; + } - /* Need to allocate a bigger buffer for bigger brush size */ - ss->texcache_side = 2 * radius; - if (!ss->texcache || ss->texcache_side > ss->texcache_actual) { - ss->texcache = BKE_brush_gen_texture_cache(brush, radius, false); - ss->texcache_actual = ss->texcache_side; - ss->tex_pool = BKE_image_pool_new(); - } + /* Need to allocate a bigger buffer for bigger brush size */ + ss->texcache_side = 2 * radius; + if (!ss->texcache || ss->texcache_side > ss->texcache_actual) { + ss->texcache = BKE_brush_gen_texture_cache(brush, radius, false); + ss->texcache_actual = ss->texcache_side; + ss->tex_pool = BKE_image_pool_new(); + } } - - bool sculpt_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - return ob && ob->mode & OB_MODE_SCULPT; + Object *ob = CTX_data_active_object(C); + return ob && ob->mode & OB_MODE_SCULPT; } bool sculpt_mode_poll_view3d(bContext *C) { - return (sculpt_mode_poll(C) && - CTX_wm_region_view3d(C)); + return (sculpt_mode_poll(C) && CTX_wm_region_view3d(C)); } bool sculpt_poll_view3d(bContext *C) { - return (sculpt_poll(C) && - CTX_wm_region_view3d(C)); + return (sculpt_poll(C) && CTX_wm_region_view3d(C)); } bool sculpt_poll(bContext *C) { - return sculpt_mode_poll(C) && paint_poll(C); + return sculpt_mode_poll(C) && paint_poll(C); } static const char *sculpt_tool_name(Sculpt *sd) { - Brush *brush = BKE_paint_brush(&sd->paint); - - switch ((eBrushSculptTool)brush->sculpt_tool) { - case SCULPT_TOOL_DRAW: - return "Draw Brush"; - case SCULPT_TOOL_SMOOTH: - return "Smooth Brush"; - case SCULPT_TOOL_CREASE: - return "Crease Brush"; - case SCULPT_TOOL_BLOB: - return "Blob Brush"; - case SCULPT_TOOL_PINCH: - return "Pinch Brush"; - case SCULPT_TOOL_INFLATE: - return "Inflate Brush"; - case SCULPT_TOOL_GRAB: - return "Grab Brush"; - case SCULPT_TOOL_NUDGE: - return "Nudge Brush"; - case SCULPT_TOOL_THUMB: - return "Thumb Brush"; - case SCULPT_TOOL_LAYER: - return "Layer Brush"; - case SCULPT_TOOL_FLATTEN: - return "Flatten Brush"; - case SCULPT_TOOL_CLAY: - return "Clay Brush"; - case SCULPT_TOOL_CLAY_STRIPS: - return "Clay Strips Brush"; - case SCULPT_TOOL_FILL: - return "Fill Brush"; - case SCULPT_TOOL_SCRAPE: - return "Scrape Brush"; - case SCULPT_TOOL_SNAKE_HOOK: - return "Snake Hook Brush"; - case SCULPT_TOOL_ROTATE: - return "Rotate Brush"; - case SCULPT_TOOL_MASK: - return "Mask Brush"; - case SCULPT_TOOL_SIMPLIFY: - return "Simplify Brush"; - } - - return "Sculpting"; + Brush *brush = BKE_paint_brush(&sd->paint); + + switch ((eBrushSculptTool)brush->sculpt_tool) { + case SCULPT_TOOL_DRAW: + return "Draw Brush"; + case SCULPT_TOOL_SMOOTH: + return "Smooth Brush"; + case SCULPT_TOOL_CREASE: + return "Crease Brush"; + case SCULPT_TOOL_BLOB: + return "Blob Brush"; + case SCULPT_TOOL_PINCH: + return "Pinch Brush"; + case SCULPT_TOOL_INFLATE: + return "Inflate Brush"; + case SCULPT_TOOL_GRAB: + return "Grab Brush"; + case SCULPT_TOOL_NUDGE: + return "Nudge Brush"; + case SCULPT_TOOL_THUMB: + return "Thumb Brush"; + case SCULPT_TOOL_LAYER: + return "Layer Brush"; + case SCULPT_TOOL_FLATTEN: + return "Flatten Brush"; + case SCULPT_TOOL_CLAY: + return "Clay Brush"; + case SCULPT_TOOL_CLAY_STRIPS: + return "Clay Strips Brush"; + case SCULPT_TOOL_FILL: + return "Fill Brush"; + case SCULPT_TOOL_SCRAPE: + return "Scrape Brush"; + case SCULPT_TOOL_SNAKE_HOOK: + return "Snake Hook Brush"; + case SCULPT_TOOL_ROTATE: + return "Rotate Brush"; + case SCULPT_TOOL_MASK: + return "Mask Brush"; + case SCULPT_TOOL_SIMPLIFY: + return "Simplify Brush"; + } + + return "Sculpting"; } /** @@ -4347,567 +4383,557 @@ static const char *sculpt_tool_name(Sculpt *sd) void sculpt_cache_free(StrokeCache *cache) { - if (cache->dial) - MEM_freeN(cache->dial); - MEM_freeN(cache); + if (cache->dial) + MEM_freeN(cache->dial); + MEM_freeN(cache); } /* Initialize mirror modifier clipping */ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) { - ModifierData *md; - int i; - - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Mirror && - (md->mode & eModifierMode_Realtime)) - { - MirrorModifierData *mmd = (MirrorModifierData *)md; - - if (mmd->flag & MOD_MIR_CLIPPING) { - /* check each axis for mirroring */ - for (i = 0; i < 3; ++i) { - if (mmd->flag & (MOD_MIR_AXIS_X << i)) { - /* enable sculpt clipping */ - ss->cache->flag |= CLIP_X << i; - - /* update the clip tolerance */ - if (mmd->tolerance > - ss->cache->clip_tolerance[i]) - { - ss->cache->clip_tolerance[i] = - mmd->tolerance; - } - } - } - } - } - } + ModifierData *md; + int i; + + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + /* check each axis for mirroring */ + for (i = 0; i < 3; ++i) { + if (mmd->flag & (MOD_MIR_AXIS_X << i)) { + /* enable sculpt clipping */ + ss->cache->flag |= CLIP_X << i; + + /* update the clip tolerance */ + if (mmd->tolerance > ss->cache->clip_tolerance[i]) { + ss->cache->clip_tolerance[i] = mmd->tolerance; + } + } + } + } + } + } } /* Initialize the stroke cache invariants from operator properties */ static void sculpt_update_cache_invariants( - bContext *C, Sculpt *sd, SculptSession *ss, - wmOperator *op, const float mouse[2]) -{ - StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - Brush *brush = BKE_paint_brush(&sd->paint); - ViewContext *vc = paint_stroke_view_context(op->customdata); - Object *ob = CTX_data_active_object(C); - float mat[3][3]; - float viewDir[3] = {0.0f, 0.0f, 1.0f}; - float max_scale; - int i; - int mode; - - ss->cache = cache; - - /* Set scaling adjustment */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - max_scale = 1.0f; - } - else { - max_scale = 0.0f; - for (i = 0; i < 3; i ++) { - max_scale = max_ff(max_scale, fabsf(ob->scale[i])); - } - } - cache->scale[0] = max_scale / ob->scale[0]; - cache->scale[1] = max_scale / ob->scale[1]; - cache->scale[2] = max_scale / ob->scale[2]; - - cache->plane_trim_squared = brush->plane_trim * brush->plane_trim; - - cache->flag = 0; - - sculpt_init_mirror_clipping(ob, ss); - - /* Initial mouse location */ - if (mouse) - copy_v2_v2(cache->initial_mouse, mouse); - else - zero_v2(cache->initial_mouse); - - mode = RNA_enum_get(op->ptr, "mode"); - cache->invert = mode == BRUSH_STROKE_INVERT; - cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; - cache->normal_weight = brush->normal_weight; - - /* interpret invert as following normal, for grab brushes */ - if (SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool)) { - if (cache->invert) { - cache->invert = false; - cache->normal_weight = (cache->normal_weight == 0.0f); - } - } - - /* not very nice, but with current events system implementation - * we can't handle brush appearance inversion hotkey separately (sergey) */ - if (cache->invert) ups->draw_inverted = true; - else ups->draw_inverted = false; - - /* Alt-Smooth */ - if (cache->alt_smooth) { - if (brush->sculpt_tool == SCULPT_TOOL_MASK) { - cache->saved_mask_brush_tool = brush->mask_tool; - brush->mask_tool = BRUSH_MASK_SMOOTH; - } - else { - Paint *p = &sd->paint; - Brush *br; - int size = BKE_brush_size_get(scene, brush); - - BLI_strncpy(cache->saved_active_brush_name, brush->id.name + 2, - sizeof(cache->saved_active_brush_name)); - - br = (Brush *)BKE_libblock_find_name(bmain, ID_BR, "Smooth"); - if (br) { - BKE_paint_brush_set(p, br); - brush = br; - cache->saved_smooth_size = BKE_brush_size_get(scene, brush); - BKE_brush_size_set(scene, brush, size); - curvemapping_initialize(brush->curve); - } - } - } - - copy_v2_v2(cache->mouse, cache->initial_mouse); - copy_v2_v2(ups->tex_mouse, cache->initial_mouse); - - /* Truly temporary data that isn't stored in properties */ - - cache->vc = vc; - - cache->brush = brush; - - /* cache projection matrix */ - ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); - - invert_m4_m4(ob->imat, ob->obmat); - copy_m3_m4(mat, cache->vc->rv3d->viewinv); - mul_m3_v3(mat, viewDir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, viewDir); - normalize_v3_v3(cache->true_view_normal, viewDir); - - cache->supports_gravity = (!ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY) && - (sd->gravity_factor > 0.0f)); - /* get gravity vector in world space */ - if (cache->supports_gravity) { - if (sd->gravity_object) { - Object *gravity_object = sd->gravity_object; - - copy_v3_v3(cache->true_gravity_direction, gravity_object->obmat[2]); - } - else { - cache->true_gravity_direction[0] = cache->true_gravity_direction[1] = 0.0; - cache->true_gravity_direction[2] = 1.0; - } - - /* transform to sculpted object space */ - mul_m3_v3(mat, cache->true_gravity_direction); - normalize_v3(cache->true_gravity_direction); - } - - /* Initialize layer brush displacements and persistent coords */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - /* not supported yet for multires or dynamic topology */ - if (!ss->multires && !ss->bm && !ss->layer_co && - (brush->flag & BRUSH_PERSISTENT)) - { - if (!ss->layer_co) - ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, - "sculpt mesh vertices copy"); - - if (ss->deform_cos) { - memcpy(ss->layer_co, ss->deform_cos, ss->totvert); - } - else { - for (i = 0; i < ss->totvert; ++i) { - copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); - } - } - } - - if (ss->bm) { - /* Free any remaining layer displacements from nodes. If not and topology changes - * from using another tool, then next layer toolstroke - * can access past disp array bounds */ - BKE_pbvh_free_layer_disp(ss->pbvh); - } - } - - /* Make copies of the mesh vertex locations and normals for some tools */ - if (brush->flag & BRUSH_ANCHORED) { - cache->original = 1; - } - - if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { - if (!(brush->flag & BRUSH_ACCUMULATE)) { - cache->original = 1; - } - } - - cache->first_time = 1; + bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2]) +{ + StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *brush = BKE_paint_brush(&sd->paint); + ViewContext *vc = paint_stroke_view_context(op->customdata); + Object *ob = CTX_data_active_object(C); + float mat[3][3]; + float viewDir[3] = {0.0f, 0.0f, 1.0f}; + float max_scale; + int i; + int mode; + + ss->cache = cache; + + /* Set scaling adjustment */ + if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + max_scale = 1.0f; + } + else { + max_scale = 0.0f; + for (i = 0; i < 3; i++) { + max_scale = max_ff(max_scale, fabsf(ob->scale[i])); + } + } + cache->scale[0] = max_scale / ob->scale[0]; + cache->scale[1] = max_scale / ob->scale[1]; + cache->scale[2] = max_scale / ob->scale[2]; + + cache->plane_trim_squared = brush->plane_trim * brush->plane_trim; + + cache->flag = 0; + + sculpt_init_mirror_clipping(ob, ss); + + /* Initial mouse location */ + if (mouse) + copy_v2_v2(cache->initial_mouse, mouse); + else + zero_v2(cache->initial_mouse); + + mode = RNA_enum_get(op->ptr, "mode"); + cache->invert = mode == BRUSH_STROKE_INVERT; + cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; + cache->normal_weight = brush->normal_weight; + + /* interpret invert as following normal, for grab brushes */ + if (SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool)) { + if (cache->invert) { + cache->invert = false; + cache->normal_weight = (cache->normal_weight == 0.0f); + } + } + + /* not very nice, but with current events system implementation + * we can't handle brush appearance inversion hotkey separately (sergey) */ + if (cache->invert) + ups->draw_inverted = true; + else + ups->draw_inverted = false; + + /* Alt-Smooth */ + if (cache->alt_smooth) { + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + cache->saved_mask_brush_tool = brush->mask_tool; + brush->mask_tool = BRUSH_MASK_SMOOTH; + } + else { + Paint *p = &sd->paint; + Brush *br; + int size = BKE_brush_size_get(scene, brush); + + BLI_strncpy(cache->saved_active_brush_name, + brush->id.name + 2, + sizeof(cache->saved_active_brush_name)); + + br = (Brush *)BKE_libblock_find_name(bmain, ID_BR, "Smooth"); + if (br) { + BKE_paint_brush_set(p, br); + brush = br; + cache->saved_smooth_size = BKE_brush_size_get(scene, brush); + BKE_brush_size_set(scene, brush, size); + curvemapping_initialize(brush->curve); + } + } + } + + copy_v2_v2(cache->mouse, cache->initial_mouse); + copy_v2_v2(ups->tex_mouse, cache->initial_mouse); + + /* Truly temporary data that isn't stored in properties */ + + cache->vc = vc; + + cache->brush = brush; + + /* cache projection matrix */ + ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); + + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, cache->vc->rv3d->viewinv); + mul_m3_v3(mat, viewDir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, viewDir); + normalize_v3_v3(cache->true_view_normal, viewDir); + + cache->supports_gravity = + (!ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY) && + (sd->gravity_factor > 0.0f)); + /* get gravity vector in world space */ + if (cache->supports_gravity) { + if (sd->gravity_object) { + Object *gravity_object = sd->gravity_object; + + copy_v3_v3(cache->true_gravity_direction, gravity_object->obmat[2]); + } + else { + cache->true_gravity_direction[0] = cache->true_gravity_direction[1] = 0.0; + cache->true_gravity_direction[2] = 1.0; + } + + /* transform to sculpted object space */ + mul_m3_v3(mat, cache->true_gravity_direction); + normalize_v3(cache->true_gravity_direction); + } + + /* Initialize layer brush displacements and persistent coords */ + if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + /* not supported yet for multires or dynamic topology */ + if (!ss->multires && !ss->bm && !ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + if (!ss->layer_co) + ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); + + if (ss->deform_cos) { + memcpy(ss->layer_co, ss->deform_cos, ss->totvert); + } + else { + for (i = 0; i < ss->totvert; ++i) { + copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); + } + } + } + + if (ss->bm) { + /* Free any remaining layer displacements from nodes. If not and topology changes + * from using another tool, then next layer toolstroke + * can access past disp array bounds */ + BKE_pbvh_free_layer_disp(ss->pbvh); + } + } + + /* Make copies of the mesh vertex locations and normals for some tools */ + if (brush->flag & BRUSH_ANCHORED) { + cache->original = 1; + } + + if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { + if (!(brush->flag & BRUSH_ACCUMULATE)) { + cache->original = 1; + } + } + + cache->first_time = 1; #define PIXEL_INPUT_THRESHHOLD 5 - if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) - cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); + if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) + cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); #undef PIXEL_INPUT_THRESHHOLD } static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Brush *brush) { - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const float mouse[2] = { - cache->mouse[0], - cache->mouse[1], - }; - int tool = brush->sculpt_tool; - - if (ELEM(tool, - SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE, - SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, - SCULPT_TOOL_THUMB) || - sculpt_brush_use_topology_rake(ss, brush)) - { - float grab_location[3], imat[4][4], delta[3], loc[3]; - - if (cache->first_time) { - copy_v3_v3(cache->orig_grab_location, - cache->true_location); - } - else if (tool == SCULPT_TOOL_SNAKE_HOOK) - add_v3_v3(cache->true_location, cache->grab_delta); - - /* compute 3d coordinate at same z from original location + mouse */ - mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); - ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->ar, loc, mouse, grab_location); - - /* compute delta to move verts by */ - if (!cache->first_time) { - switch (tool) { - case SCULPT_TOOL_GRAB: - case SCULPT_TOOL_THUMB: - sub_v3_v3v3(delta, grab_location, cache->old_grab_location); - invert_m4_m4(imat, ob->obmat); - mul_mat3_m4_v3(imat, delta); - add_v3_v3(cache->grab_delta, delta); - break; - case SCULPT_TOOL_CLAY_STRIPS: - case SCULPT_TOOL_NUDGE: - case SCULPT_TOOL_SNAKE_HOOK: - if (brush->flag & BRUSH_ANCHORED) { - float orig[3]; - mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location); - sub_v3_v3v3(cache->grab_delta, grab_location, orig); - } - else { - sub_v3_v3v3(cache->grab_delta, grab_location, - cache->old_grab_location); - } - invert_m4_m4(imat, ob->obmat); - mul_mat3_m4_v3(imat, cache->grab_delta); - break; - default: - /* Use for 'Brush.topology_rake_factor'. */ - sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); - break; - - } - } - else { - zero_v3(cache->grab_delta); - } - - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(cache->grab_delta, cache->grab_delta, ss->cache->true_view_normal); - } - - copy_v3_v3(cache->old_grab_location, grab_location); - - if (tool == SCULPT_TOOL_GRAB) - copy_v3_v3(cache->anchored_location, cache->true_location); - else if (tool == SCULPT_TOOL_THUMB) - copy_v3_v3(cache->anchored_location, cache->orig_grab_location); - - if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { - /* location stays the same for finding vertices in brush radius */ - copy_v3_v3(cache->true_location, cache->orig_grab_location); - - ups->draw_anchored = true; - copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); - ups->anchored_size = ups->pixel_radius; - } - - - /* handle 'rake' */ - cache->is_rake_rotation_valid = false; - - if (cache->first_time) { - copy_v3_v3(cache->rake_data.follow_co, grab_location); - } - - if (sculpt_brush_needs_rake_rotation(brush)) { - cache->rake_data.follow_dist = cache->radius * SCULPT_RAKE_BRUSH_FACTOR; - - if (!is_zero_v3(cache->grab_delta)) { - const float eps = 0.00001f; - - float v1[3], v2[3]; - - copy_v3_v3(v1, cache->rake_data.follow_co); - copy_v3_v3(v2, cache->rake_data.follow_co); - sub_v3_v3(v2, cache->grab_delta); - - sub_v3_v3(v1, grab_location); - sub_v3_v3(v2, grab_location); - - if ((normalize_v3(v2) > eps) && - (normalize_v3(v1) > eps) && - (len_squared_v3v3(v1, v2) > eps)) - { - const float rake_dist_sq = len_squared_v3v3(cache->rake_data.follow_co, grab_location); - const float rake_fade = (rake_dist_sq > SQUARE(cache->rake_data.follow_dist)) ? - 1.0f : sqrtf(rake_dist_sq) / cache->rake_data.follow_dist; - - float axis[3], angle; - float tquat[4]; - - rotation_between_vecs_to_quat(tquat, v1, v2); - - /* use axis-angle to scale rotation since the factor may be above 1 */ - quat_to_axis_angle(axis, &angle, tquat); - normalize_v3(axis); - - angle *= brush->rake_factor * rake_fade; - axis_angle_normalized_to_quat(cache->rake_rotation, axis, angle); - cache->is_rake_rotation_valid = true; - } - } - sculpt_rake_data_update(&cache->rake_data, grab_location); - } - } + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const float mouse[2] = { + cache->mouse[0], + cache->mouse[1], + }; + int tool = brush->sculpt_tool; + + if (ELEM(tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_NUDGE, + SCULPT_TOOL_CLAY_STRIPS, + SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_THUMB) || + sculpt_brush_use_topology_rake(ss, brush)) { + float grab_location[3], imat[4][4], delta[3], loc[3]; + + if (cache->first_time) { + copy_v3_v3(cache->orig_grab_location, cache->true_location); + } + else if (tool == SCULPT_TOOL_SNAKE_HOOK) + add_v3_v3(cache->true_location, cache->grab_delta); + + /* compute 3d coordinate at same z from original location + mouse */ + mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); + ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->ar, loc, mouse, grab_location); + + /* compute delta to move verts by */ + if (!cache->first_time) { + switch (tool) { + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_THUMB: + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + break; + case SCULPT_TOOL_CLAY_STRIPS: + case SCULPT_TOOL_NUDGE: + case SCULPT_TOOL_SNAKE_HOOK: + if (brush->flag & BRUSH_ANCHORED) { + float orig[3]; + mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location); + sub_v3_v3v3(cache->grab_delta, grab_location, orig); + } + else { + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + } + invert_m4_m4(imat, ob->obmat); + mul_mat3_m4_v3(imat, cache->grab_delta); + break; + default: + /* Use for 'Brush.topology_rake_factor'. */ + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + break; + } + } + else { + zero_v3(cache->grab_delta); + } + + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(cache->grab_delta, cache->grab_delta, ss->cache->true_view_normal); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + if (tool == SCULPT_TOOL_GRAB) + copy_v3_v3(cache->anchored_location, cache->true_location); + else if (tool == SCULPT_TOOL_THUMB) + copy_v3_v3(cache->anchored_location, cache->orig_grab_location); + + if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + /* location stays the same for finding vertices in brush radius */ + copy_v3_v3(cache->true_location, cache->orig_grab_location); + + ups->draw_anchored = true; + copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); + ups->anchored_size = ups->pixel_radius; + } + + /* handle 'rake' */ + cache->is_rake_rotation_valid = false; + + if (cache->first_time) { + copy_v3_v3(cache->rake_data.follow_co, grab_location); + } + + if (sculpt_brush_needs_rake_rotation(brush)) { + cache->rake_data.follow_dist = cache->radius * SCULPT_RAKE_BRUSH_FACTOR; + + if (!is_zero_v3(cache->grab_delta)) { + const float eps = 0.00001f; + + float v1[3], v2[3]; + + copy_v3_v3(v1, cache->rake_data.follow_co); + copy_v3_v3(v2, cache->rake_data.follow_co); + sub_v3_v3(v2, cache->grab_delta); + + sub_v3_v3(v1, grab_location); + sub_v3_v3(v2, grab_location); + + if ((normalize_v3(v2) > eps) && (normalize_v3(v1) > eps) && + (len_squared_v3v3(v1, v2) > eps)) { + const float rake_dist_sq = len_squared_v3v3(cache->rake_data.follow_co, grab_location); + const float rake_fade = (rake_dist_sq > SQUARE(cache->rake_data.follow_dist)) ? + 1.0f : + sqrtf(rake_dist_sq) / cache->rake_data.follow_dist; + + float axis[3], angle; + float tquat[4]; + + rotation_between_vecs_to_quat(tquat, v1, v2); + + /* use axis-angle to scale rotation since the factor may be above 1 */ + quat_to_axis_angle(axis, &angle, tquat); + normalize_v3(axis); + + angle *= brush->rake_factor * rake_fade; + axis_angle_normalized_to_quat(cache->rake_rotation, axis, angle); + cache->is_rake_rotation_valid = true; + } + } + sculpt_rake_data_update(&cache->rake_data, grab_location); + } + } } /* Initialize the stroke cache variants from operator properties */ -static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, - PointerRNA *ptr) -{ - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - Brush *brush = BKE_paint_brush(&sd->paint); - - /* RNA_float_get_array(ptr, "location", cache->traced_location); */ - - if (cache->first_time || - !((brush->flag & BRUSH_ANCHORED) || - (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) || - (brush->sculpt_tool == SCULPT_TOOL_ROTATE)) - ) - { - RNA_float_get_array(ptr, "location", cache->true_location); - } - - cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); - RNA_float_get_array(ptr, "mouse", cache->mouse); - - /* XXX: Use pressure value from first brush step for brushes which don't - * support strokes (grab, thumb). They depends on initial state and - * brush coord/pressure/etc. - * It's more an events design issue, which doesn't split coordinate/pressure/angle - * changing events. We should avoid this after events system re-design */ - if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { - cache->pressure = RNA_float_get(ptr, "pressure"); - } - - /* Truly temporary data that isn't stored in properties */ - if (cache->first_time) { - if (!BKE_brush_use_locked_size(scene, brush)) { - cache->initial_radius = paint_calc_object_space_radius(cache->vc, - cache->true_location, - BKE_brush_size_get(scene, brush)); - BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); - } - else { - cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); - } - } - - if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { - cache->radius = cache->initial_radius * cache->pressure; - } - else { - cache->radius = cache->initial_radius; - } - - cache->radius_squared = cache->radius * cache->radius; - - if (brush->flag & BRUSH_ANCHORED) { - /* true location has been calculated as part of the stroke system already here */ - if (brush->flag & BRUSH_EDGE_TO_EDGE) { - RNA_float_get_array(ptr, "location", cache->true_location); - } - - cache->radius = paint_calc_object_space_radius(cache->vc, - cache->true_location, - ups->pixel_radius); - cache->radius_squared = cache->radius * cache->radius; - - copy_v3_v3(cache->anchored_location, cache->true_location); - } - - sculpt_update_brush_delta(ups, ob, brush); - - if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { - cache->vertex_rotation = -BLI_dial_angle(cache->dial, cache->mouse) * cache->bstrength; - - ups->draw_anchored = true; - copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); - copy_v3_v3(cache->anchored_location, cache->true_location); - ups->anchored_size = ups->pixel_radius; - } - - cache->special_rotation = ups->brush_rotation; +static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + Brush *brush = BKE_paint_brush(&sd->paint); + + /* RNA_float_get_array(ptr, "location", cache->traced_location); */ + + if (cache->first_time || + !((brush->flag & BRUSH_ANCHORED) || (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) || + (brush->sculpt_tool == SCULPT_TOOL_ROTATE))) { + RNA_float_get_array(ptr, "location", cache->true_location); + } + + cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); + RNA_float_get_array(ptr, "mouse", cache->mouse); + + /* XXX: Use pressure value from first brush step for brushes which don't + * support strokes (grab, thumb). They depends on initial state and + * brush coord/pressure/etc. + * It's more an events design issue, which doesn't split coordinate/pressure/angle + * changing events. We should avoid this after events system re-design */ + if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { + cache->pressure = RNA_float_get(ptr, "pressure"); + } + + /* Truly temporary data that isn't stored in properties */ + if (cache->first_time) { + if (!BKE_brush_use_locked_size(scene, brush)) { + cache->initial_radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); + } + else { + cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); + } + } + + if (BKE_brush_use_size_pressure(scene, brush) && + paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { + cache->radius = cache->initial_radius * cache->pressure; + } + else { + cache->radius = cache->initial_radius; + } + + cache->radius_squared = cache->radius * cache->radius; + + if (brush->flag & BRUSH_ANCHORED) { + /* true location has been calculated as part of the stroke system already here */ + if (brush->flag & BRUSH_EDGE_TO_EDGE) { + RNA_float_get_array(ptr, "location", cache->true_location); + } + + cache->radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, ups->pixel_radius); + cache->radius_squared = cache->radius * cache->radius; + + copy_v3_v3(cache->anchored_location, cache->true_location); + } + + sculpt_update_brush_delta(ups, ob, brush); + + if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + cache->vertex_rotation = -BLI_dial_angle(cache->dial, cache->mouse) * cache->bstrength; + + ups->draw_anchored = true; + copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); + copy_v3_v3(cache->anchored_location, cache->true_location); + ups->anchored_size = ups->pixel_radius; + } + + cache->special_rotation = ups->brush_rotation; } /* Returns true if any of the smoothing modes are active (currently * one of smooth brush, autosmooth, mask smooth, or shift-key * smooth) */ -static bool sculpt_any_smooth_mode(const Brush *brush, - StrokeCache *cache, - int stroke_mode) +static bool sculpt_any_smooth_mode(const Brush *brush, StrokeCache *cache, int stroke_mode) { - return ((stroke_mode == BRUSH_STROKE_SMOOTH) || - (cache && cache->alt_smooth) || - (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || - (brush->autosmooth_factor > 0) || - ((brush->sculpt_tool == SCULPT_TOOL_MASK) && - (brush->mask_tool == BRUSH_MASK_SMOOTH))); + return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (cache && cache->alt_smooth) || + (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) || + ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH))); } static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) { - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->sculpt; - if (ss->kb || ss->modifiers_active) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Sculpt *sd = scene->toolsettings->sculpt; - bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, need_pmap, false); - } + if (ss->kb || ss->modifiers_active) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Sculpt *sd = scene->toolsettings->sculpt; + bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, need_pmap, false); + } } static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) { - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptRaycastData *srd = data_v; - float (*origco)[3] = NULL; - bool use_origco = false; - - if (srd->original && srd->ss->cache) { - if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { - use_origco = true; - } - else { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode = sculpt_undo_get_node(node); - origco = (unode) ? unode->co : NULL; - use_origco = origco ? true : false; - } - } - - if (BKE_pbvh_node_raycast(srd->ss->pbvh, node, origco, use_origco, - srd->ray_start, srd->ray_normal, &srd->depth)) - { - srd->hit = 1; - *tmin = srd->depth; - } - } + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptRaycastData *srd = data_v; + float(*origco)[3] = NULL; + bool use_origco = false; + + if (srd->original && srd->ss->cache) { + if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { + use_origco = true; + } + else { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode = sculpt_undo_get_node(node); + origco = (unode) ? unode->co : NULL; + use_origco = origco ? true : false; + } + } + + if (BKE_pbvh_node_raycast(srd->ss->pbvh, + node, + origco, + use_origco, + srd->ray_start, + srd->ray_normal, + &srd->depth)) { + srd->hit = 1; + *tmin = srd->depth; + } + } } static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *tmin) { - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptFindNearestToRayData *srd = data_v; - float (*origco)[3] = NULL; - bool use_origco = false; - - if (srd->original && srd->ss->cache) { - if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { - use_origco = true; - } - else { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode = sculpt_undo_get_node(node); - origco = (unode) ? unode->co : NULL; - use_origco = origco ? true : false; - } - } - - if (BKE_pbvh_node_find_nearest_to_ray( - srd->ss->pbvh, node, origco, use_origco, - srd->ray_start, srd->ray_normal, - &srd->depth, &srd->dist_sq_to_ray)) - { - srd->hit = 1; - *tmin = srd->dist_sq_to_ray; - } - } + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptFindNearestToRayData *srd = data_v; + float(*origco)[3] = NULL; + bool use_origco = false; + + if (srd->original && srd->ss->cache) { + if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { + use_origco = true; + } + else { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode = sculpt_undo_get_node(node); + origco = (unode) ? unode->co : NULL; + use_origco = origco ? true : false; + } + } + + if (BKE_pbvh_node_find_nearest_to_ray(srd->ss->pbvh, + node, + origco, + use_origco, + srd->ray_start, + srd->ray_normal, + &srd->depth, + &srd->dist_sq_to_ray)) { + srd->hit = 1; + *tmin = srd->dist_sq_to_ray; + } + } } static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) { - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptDetailRaycastData *srd = data_v; - if (BKE_pbvh_bmesh_node_raycast_detail(node, srd->ray_start, srd->ray_normal, - &srd->depth, &srd->edge_length)) - { - srd->hit = 1; - *tmin = srd->depth; - } - } + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptDetailRaycastData *srd = data_v; + if (BKE_pbvh_bmesh_node_raycast_detail( + node, srd->ray_start, srd->ray_normal, &srd->depth, &srd->edge_length)) { + srd->hit = 1; + *tmin = srd->depth; + } + } } -static float sculpt_raycast_init( - ViewContext *vc, const float mouse[2], - float ray_start[3], float ray_end[3], float ray_normal[3], bool original) +static float sculpt_raycast_init(ViewContext *vc, + const float mouse[2], + float ray_start[3], + float ray_end[3], + float ray_normal[3], + bool original) { - float obimat[4][4]; - float dist; - Object *ob = vc->obact; - RegionView3D *rv3d = vc->ar->regiondata; + float obimat[4][4]; + float dist; + Object *ob = vc->obact; + RegionView3D *rv3d = vc->ar->regiondata; - /* TODO: what if the segment is totally clipped? (return == 0) */ - ED_view3d_win_to_segment_clipped(vc->depsgraph, vc->ar, vc->v3d, mouse, ray_start, ray_end, true); + /* TODO: what if the segment is totally clipped? (return == 0) */ + ED_view3d_win_to_segment_clipped( + vc->depsgraph, vc->ar, vc->v3d, mouse, ray_start, ray_end, true); - invert_m4_m4(obimat, ob->obmat); - mul_m4_v3(obimat, ray_start); - mul_m4_v3(obimat, ray_end); + invert_m4_m4(obimat, ob->obmat); + mul_m4_v3(obimat, ray_start); + mul_m4_v3(obimat, ray_end); - sub_v3_v3v3(ray_normal, ray_end, ray_start); - dist = normalize_v3(ray_normal); + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); - if ((rv3d->is_persp == false) && - /* if the ray is clipped, don't adjust its start/end */ - ((rv3d->rflag & RV3D_CLIPPING) == 0)) - { - BKE_pbvh_raycast_project_ray_root(ob->sculpt->pbvh, original, ray_start, ray_end, ray_normal); + if ((rv3d->is_persp == false) && + /* if the ray is clipped, don't adjust its start/end */ + ((rv3d->rflag & RV3D_CLIPPING) == 0)) { + BKE_pbvh_raycast_project_ray_root(ob->sculpt->pbvh, original, ray_start, ray_end, ray_normal); - /* recalculate the normal */ - sub_v3_v3v3(ray_normal, ray_end, ray_start); - dist = normalize_v3(ray_normal); - } + /* recalculate the normal */ + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); + } - return dist; + return dist; } /* Do a raycast in the tree to find the 3d brush location @@ -4916,859 +4942,887 @@ static float sculpt_raycast_init( */ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) { - Object *ob; - SculptSession *ss; - StrokeCache *cache; - float ray_start[3], ray_end[3], ray_normal[3], depth; - bool original; - ViewContext vc; - - ED_view3d_viewcontext_init(C, &vc); - - ob = vc.obact; - - ss = ob->sculpt; - cache = ss->cache; - original = (cache) ? cache->original : 0; - - const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - - sculpt_stroke_modifiers_check(C, ob, brush); - - depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); - - bool hit = false; - { - SculptRaycastData srd = { - .original = original, - .ss = ob->sculpt, - .hit = 0, - .ray_start = ray_start, - .ray_normal = ray_normal, - .depth = depth, - }; - BKE_pbvh_raycast( - ss->pbvh, sculpt_raycast_cb, &srd, - ray_start, ray_normal, srd.original); - if (srd.hit) { - hit = true; - copy_v3_v3(out, ray_normal); - mul_v3_fl(out, srd.depth); - add_v3_v3(out, ray_start); - } - } - - if (hit == false) { - if (ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) { - SculptFindNearestToRayData srd = { - .original = original, - .ss = ob->sculpt, - .hit = 0, - .ray_start = ray_start, - .ray_normal = ray_normal, - .depth = FLT_MAX, - .dist_sq_to_ray = FLT_MAX, - }; - BKE_pbvh_find_nearest_to_ray( - ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, - ray_start, ray_normal, srd.original); - if (srd.hit) { - hit = true; - copy_v3_v3(out, ray_normal); - mul_v3_fl(out, srd.depth); - add_v3_v3(out, ray_start); - } - } - } - - if (cache && hit) { - copy_v3_v3(cache->true_location, out); - } - - return hit; + Object *ob; + SculptSession *ss; + StrokeCache *cache; + float ray_start[3], ray_end[3], ray_normal[3], depth; + bool original; + ViewContext vc; + + ED_view3d_viewcontext_init(C, &vc); + + ob = vc.obact; + + ss = ob->sculpt; + cache = ss->cache; + original = (cache) ? cache->original : 0; + + const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + + sculpt_stroke_modifiers_check(C, ob, brush); + + depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + + bool hit = false; + { + SculptRaycastData srd = { + .original = original, + .ss = ob->sculpt, + .hit = 0, + .ray_start = ray_start, + .ray_normal = ray_normal, + .depth = depth, + }; + BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + if (srd.hit) { + hit = true; + copy_v3_v3(out, ray_normal); + mul_v3_fl(out, srd.depth); + add_v3_v3(out, ray_start); + } + } + + if (hit == false) { + if (ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) { + SculptFindNearestToRayData srd = { + .original = original, + .ss = ob->sculpt, + .hit = 0, + .ray_start = ray_start, + .ray_normal = ray_normal, + .depth = FLT_MAX, + .dist_sq_to_ray = FLT_MAX, + }; + BKE_pbvh_find_nearest_to_ray( + ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, ray_start, ray_normal, srd.original); + if (srd.hit) { + hit = true; + copy_v3_v3(out, ray_normal); + mul_v3_fl(out, srd.depth); + add_v3_v3(out, ray_start); + } + } + } + + if (cache && hit) { + copy_v3_v3(cache->true_location, out); + } + + return hit; } static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) { - Brush *brush = BKE_paint_brush(&sd->paint); - MTex *mtex = &brush->mtex; + Brush *brush = BKE_paint_brush(&sd->paint); + MTex *mtex = &brush->mtex; - /* init mtex nodes */ - if (mtex->tex && mtex->tex->nodetree) { - /* has internal flag to detect it only does it once */ - ntreeTexBeginExecTree(mtex->tex->nodetree); - } + /* init mtex nodes */ + if (mtex->tex && mtex->tex->nodetree) { + /* has internal flag to detect it only does it once */ + ntreeTexBeginExecTree(mtex->tex->nodetree); + } - /* TODO: Shouldn't really have to do this at the start of every - * stroke, but sculpt would need some sort of notification when - * changes are made to the texture. */ - sculpt_update_tex(scene, sd, ss); + /* TODO: Shouldn't really have to do this at the start of every + * stroke, but sculpt would need some sort of notification when + * changes are made to the texture. */ + sculpt_update_tex(scene, sd, ss); } static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = CTX_data_active_object(C)->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - int mode = RNA_enum_get(op->ptr, "mode"); - bool is_smooth; - bool need_mask = false; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + int mode = RNA_enum_get(op->ptr, "mode"); + bool is_smooth; + bool need_mask = false; - if (brush->sculpt_tool == SCULPT_TOOL_MASK) { - need_mask = true; - } + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + need_mask = true; + } - view3d_operator_needs_opengl(C); - sculpt_brush_init_tex(scene, sd, ss); + view3d_operator_needs_opengl(C); + sculpt_brush_init_tex(scene, sd, ss); - is_smooth = sculpt_any_smooth_mode(brush, NULL, mode); - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, is_smooth, need_mask); + is_smooth = sculpt_any_smooth_mode(brush, NULL, mode); + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, is_smooth, need_mask); } static void sculpt_restore_mesh(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - /* Restore the mesh before continuing with anchored stroke */ - if ((brush->flag & BRUSH_ANCHORED) || - (brush->sculpt_tool == SCULPT_TOOL_GRAB && - BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || - (brush->flag & BRUSH_DRAG_DOT)) - { - paint_mesh_restore_co(sd, ob); - } + /* Restore the mesh before continuing with anchored stroke */ + if ((brush->flag & BRUSH_ANCHORED) || + (brush->sculpt_tool == SCULPT_TOOL_GRAB && + BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || + (brush->flag & BRUSH_DRAG_DOT)) { + paint_mesh_restore_co(sd, ob); + } } /* Copy the PBVH bounding box into the object's bounding box */ void sculpt_update_object_bounding_box(Object *ob) { - if (ob->runtime.bb) { - float bb_min[3], bb_max[3]; + if (ob->runtime.bb) { + float bb_min[3], bb_max[3]; - BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); - BKE_boundbox_init_from_minmax(ob->runtime.bb, bb_min, bb_max); - } + BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); + BKE_boundbox_init_from_minmax(ob->runtime.bb, bb_min, bb_max); + } } static void sculpt_flush_update(bContext *C) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = CTX_data_active_object(C); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - SculptSession *ss = ob->sculpt; - ARegion *ar = CTX_wm_region(C); - bScreen *screen = CTX_wm_screen(C); - MultiresModifierData *mmd = ss->multires; - - if (mmd != NULL) { - /* NOTE: SubdivCCG is living in the evaluated object. */ - multires_mark_as_modified(ob_eval, MULTIRES_COORDS_MODIFIED); - } - - DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - - bool use_shaded_mode = false; - if (mmd || (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH)) { - /* Multres or dyntopo are drawn directly by EEVEE, - * no need for hacks in this case. */ - } - else { - /* We search if an area of the current window is in lookdev/rendered - * display mode. In this case, for changes to show up, we need to - * tag for ID_RECALC_GEOMETRY. */ - for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { - for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - if (v3d->shading.type > OB_SOLID) { - use_shaded_mode = true; - } - } - } - } - } - - if (ss->kb || ss->modifiers_active || use_shaded_mode) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(ar); - } - else { - rcti r; - - BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - /* Update the object's bounding box too so that the object - * doesn't get incorrectly clipped during drawing in - * draw_mesh_object(). [#33790] */ - sculpt_update_object_bounding_box(ob); - - if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { - if (ss->cache) { - ss->cache->current_r = r; - } - - /* previous is not set in the current cache else - * the partial rect will always grow */ - sculpt_extend_redraw_rect_previous(ob, &r); - - r.xmin += ar->winrct.xmin - 2; - r.xmax += ar->winrct.xmin + 2; - r.ymin += ar->winrct.ymin - 2; - r.ymax += ar->winrct.ymin + 2; - - ss->partial_redraw = 1; - ED_region_tag_redraw_partial(ar, &r); - } - } + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + SculptSession *ss = ob->sculpt; + ARegion *ar = CTX_wm_region(C); + bScreen *screen = CTX_wm_screen(C); + MultiresModifierData *mmd = ss->multires; + + if (mmd != NULL) { + /* NOTE: SubdivCCG is living in the evaluated object. */ + multires_mark_as_modified(ob_eval, MULTIRES_COORDS_MODIFIED); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + + bool use_shaded_mode = false; + if (mmd || (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH)) { + /* Multres or dyntopo are drawn directly by EEVEE, + * no need for hacks in this case. */ + } + else { + /* We search if an area of the current window is in lookdev/rendered + * display mode. In this case, for changes to show up, we need to + * tag for ID_RECALC_GEOMETRY. */ + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + if (v3d->shading.type > OB_SOLID) { + use_shaded_mode = true; + } + } + } + } + } + + if (ss->kb || ss->modifiers_active || use_shaded_mode) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(ar); + } + else { + rcti r; + + BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); + /* Update the object's bounding box too so that the object + * doesn't get incorrectly clipped during drawing in + * draw_mesh_object(). [#33790] */ + sculpt_update_object_bounding_box(ob); + + if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { + if (ss->cache) { + ss->cache->current_r = r; + } + + /* previous is not set in the current cache else + * the partial rect will always grow */ + sculpt_extend_redraw_rect_previous(ob, &r); + + r.xmin += ar->winrct.xmin - 2; + r.xmax += ar->winrct.xmin + 2; + r.ymin += ar->winrct.ymin - 2; + r.ymax += ar->winrct.ymin + 2; + + ss->partial_redraw = 1; + ED_region_tag_redraw_partial(ar, &r); + } + } } /* Returns whether the mouse/stylus is over the mesh (1) * or over the background (0) */ static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y) { - float mouse[2], co[3]; + float mouse[2], co[3]; - mouse[0] = x; - mouse[1] = y; + mouse[0] = x; + mouse[1] = y; - return sculpt_stroke_get_location(C, co, mouse); + return sculpt_stroke_get_location(C, co, mouse); } -static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, - const float mouse[2]) +static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) { - /* Don't start the stroke until mouse goes over the mesh. - * note: mouse will only be null when re-executing the saved stroke. - * We have exception for 'exec' strokes since they may not set 'mouse', - * only 'location', see: T52195. */ - if (((op->flag & OP_IS_INVOKE) == 0) || - (mouse == NULL) || over_mesh(C, op, mouse[0], mouse[1])) - { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + /* Don't start the stroke until mouse goes over the mesh. + * note: mouse will only be null when re-executing the saved stroke. + * We have exception for 'exec' strokes since they may not set 'mouse', + * only 'location', see: T52195. */ + if (((op->flag & OP_IS_INVOKE) == 0) || (mouse == NULL) || + over_mesh(C, op, mouse[0], mouse[1])) { + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); + ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_update_cache_invariants(C, sd, ss, op, mouse); + sculpt_update_cache_invariants(C, sd, ss, op, mouse); - sculpt_undo_push_begin(sculpt_tool_name(sd)); + sculpt_undo_push_begin(sculpt_tool_name(sd)); - return 1; - } - else - return 0; + return 1; + } + else + return 0; } -static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(stroke), PointerRNA *itemptr) +static void sculpt_stroke_update_step(bContext *C, + struct PaintStroke *UNUSED(stroke), + PointerRNA *itemptr) { - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + const Brush *brush = BKE_paint_brush(&sd->paint); - sculpt_stroke_modifiers_check(C, ob, brush); - sculpt_update_cache_variants(C, sd, ob, itemptr); - sculpt_restore_mesh(sd, ob); + sculpt_stroke_modifiers_check(C, ob, brush); + sculpt_update_cache_variants(C, sd, ob, itemptr); + sculpt_restore_mesh(sd, ob); - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); - } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); - } - else { - BKE_pbvh_bmesh_detail_size_set( - ss->pbvh, - (ss->cache->radius / - (float)ups->pixel_radius) * - (float)(sd->detail_size * U.pixelsize) / 0.4f); - } + if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { + float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + } + else { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, + (ss->cache->radius / (float)ups->pixel_radius) * + (float)(sd->detail_size * U.pixelsize) / 0.4f); + } - if (sculpt_stroke_is_dynamic_topology(ss, brush)) { - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); - } + if (sculpt_stroke_is_dynamic_topology(ss, brush)) { + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + } - do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); + do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); - sculpt_combine_proxies(sd, ob); + sculpt_combine_proxies(sd, ob); - /* hack to fix noise texture tearing mesh */ - sculpt_fix_noise_tear(sd, ob); + /* hack to fix noise texture tearing mesh */ + sculpt_fix_noise_tear(sd, ob); - /* TODO(sergey): This is not really needed for the solid shading, - * which does use pBVH drawing anyway, but texture and wireframe - * requires this. - * - * Could be optimized later, but currently don't think it's so - * much common scenario. - * - * Same applies to the DEG_id_tag_update() invoked from - * sculpt_flush_update(). - */ - if (ss->modifiers_active) { - sculpt_flush_stroke_deform(sd, ob); - } - else if (ss->kb) { - sculpt_update_keyblock(ob); - } + /* TODO(sergey): This is not really needed for the solid shading, + * which does use pBVH drawing anyway, but texture and wireframe + * requires this. + * + * Could be optimized later, but currently don't think it's so + * much common scenario. + * + * Same applies to the DEG_id_tag_update() invoked from + * sculpt_flush_update(). + */ + if (ss->modifiers_active) { + sculpt_flush_stroke_deform(sd, ob); + } + else if (ss->kb) { + sculpt_update_keyblock(ob); + } - ss->cache->first_time = false; + ss->cache->first_time = false; - /* Cleanup */ - sculpt_flush_update(C); + /* Cleanup */ + sculpt_flush_update(C); } static void sculpt_brush_exit_tex(Sculpt *sd) { - Brush *brush = BKE_paint_brush(&sd->paint); - MTex *mtex = &brush->mtex; + Brush *brush = BKE_paint_brush(&sd->paint); + MTex *mtex = &brush->mtex; - if (mtex->tex && mtex->tex->nodetree) - ntreeTexEndExecTree(mtex->tex->nodetree->execdata); + if (mtex->tex && mtex->tex->nodetree) + ntreeTexEndExecTree(mtex->tex->nodetree->execdata); } static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(stroke)) { - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - Scene *scene = CTX_data_scene(C); - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - /* Finished */ - if (ss->cache) { - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - Brush *brush = BKE_paint_brush(&sd->paint); - BLI_assert(brush == ss->cache->brush); /* const, so we shouldn't change. */ - ups->draw_inverted = false; + /* Finished */ + if (ss->cache) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *brush = BKE_paint_brush(&sd->paint); + BLI_assert(brush == ss->cache->brush); /* const, so we shouldn't change. */ + ups->draw_inverted = false; - sculpt_stroke_modifiers_check(C, ob, brush); + sculpt_stroke_modifiers_check(C, ob, brush); - /* Alt-Smooth */ - if (ss->cache->alt_smooth) { - if (brush->sculpt_tool == SCULPT_TOOL_MASK) { - brush->mask_tool = ss->cache->saved_mask_brush_tool; - } - else { - BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size); - brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, ss->cache->saved_active_brush_name); - if (brush) { - BKE_paint_brush_set(&sd->paint, brush); - } - } - } + /* Alt-Smooth */ + if (ss->cache->alt_smooth) { + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + brush->mask_tool = ss->cache->saved_mask_brush_tool; + } + else { + BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size); + brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, ss->cache->saved_active_brush_name); + if (brush) { + BKE_paint_brush_set(&sd->paint, brush); + } + } + } - sculpt_cache_free(ss->cache); - ss->cache = NULL; + sculpt_cache_free(ss->cache); + ss->cache = NULL; - sculpt_undo_push_end(); + sculpt_undo_push_end(); - BKE_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); + BKE_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) - BKE_pbvh_bmesh_after_stroke(ss->pbvh); + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) + BKE_pbvh_bmesh_after_stroke(ss->pbvh); - /* optimization: if there is locked key and active modifiers present in */ - /* the stack, keyblock is updating at each step. otherwise we could update */ - /* keyblock only when stroke is finished */ - if (ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ob); + /* optimization: if there is locked key and active modifiers present in */ + /* the stack, keyblock is updating at each step. otherwise we could update */ + /* keyblock only when stroke is finished */ + if (ss->kb && !ss->modifiers_active) + sculpt_update_keyblock(ob); - ss->partial_redraw = 0; + ss->partial_redraw = 0; - /* try to avoid calling this, only for e.g. linked duplicates now */ - if (((Mesh *)ob->data)->id.us > 1) - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + /* try to avoid calling this, only for e.g. linked duplicates now */ + if (((Mesh *)ob->data)->id.us > 1) + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - } + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } - sculpt_brush_exit_tex(sd); + sculpt_brush_exit_tex(sd); } static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - struct PaintStroke *stroke; - int ignore_background_click; - int retval; + struct PaintStroke *stroke; + int ignore_background_click; + int retval; - sculpt_brush_stroke_init(C, op); + sculpt_brush_stroke_init(C, op); - stroke = paint_stroke_new(C, op, sculpt_stroke_get_location, - sculpt_stroke_test_start, - sculpt_stroke_update_step, NULL, - sculpt_stroke_done, event->type); + stroke = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + NULL, + sculpt_stroke_done, + event->type); - op->customdata = stroke; + op->customdata = stroke; - /* For tablet rotation */ - ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); + /* For tablet rotation */ + ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { - paint_stroke_data_free(op); - return OPERATOR_PASS_THROUGH; - } + if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { + paint_stroke_data_free(op); + return OPERATOR_PASS_THROUGH; + } - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) { - sculpt_brush_stroke_init(C, op); + sculpt_brush_stroke_init(C, op); - op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, sculpt_stroke_test_start, - sculpt_stroke_update_step, NULL, sculpt_stroke_done, 0); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + NULL, + sculpt_stroke_done, + 0); - /* frees op->customdata */ - paint_stroke_exec(C, op); + /* frees op->customdata */ + paint_stroke_exec(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + const Brush *brush = BKE_paint_brush(&sd->paint); - /* XXX Canceling strokes that way does not work with dynamic topology, user will have to do real undo for now. - * See T46456. */ - if (ss->cache && !sculpt_stroke_is_dynamic_topology(ss, brush)) { - paint_mesh_restore_co(sd, ob); - } + /* XXX Canceling strokes that way does not work with dynamic topology, user will have to do real undo for now. + * See T46456. */ + if (ss->cache && !sculpt_stroke_is_dynamic_topology(ss, brush)) { + paint_mesh_restore_co(sd, ob); + } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op); - if (ss->cache) { - sculpt_cache_free(ss->cache); - ss->cache = NULL; - } + if (ss->cache) { + sculpt_cache_free(ss->cache); + ss->cache = NULL; + } - sculpt_brush_exit_tex(sd); + sculpt_brush_exit_tex(sd); } static void SCULPT_OT_brush_stroke(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sculpt"; - ot->idname = "SCULPT_OT_brush_stroke"; - ot->description = "Sculpt a stroke into the geometry"; + /* identifiers */ + ot->name = "Sculpt"; + ot->idname = "SCULPT_OT_brush_stroke"; + ot->description = "Sculpt a stroke into the geometry"; - /* api callbacks */ - ot->invoke = sculpt_brush_stroke_invoke; - ot->modal = paint_stroke_modal; - ot->exec = sculpt_brush_stroke_exec; - ot->poll = sculpt_poll; - ot->cancel = sculpt_brush_stroke_cancel; + /* api callbacks */ + ot->invoke = sculpt_brush_stroke_invoke; + ot->modal = paint_stroke_modal; + ot->exec = sculpt_brush_stroke_exec; + ot->poll = sculpt_poll; + ot->cancel = sculpt_brush_stroke_cancel; - /* flags (sculpt does own undo? (ton) */ - ot->flag = OPTYPE_BLOCKING; + /* flags (sculpt does own undo? (ton) */ + ot->flag = OPTYPE_BLOCKING; - /* properties */ + /* properties */ - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot); - RNA_def_boolean(ot->srna, "ignore_background_click", 0, - "Ignore Background Click", - "Clicks on the background do not start the stroke"); + RNA_def_boolean(ot->srna, + "ignore_background_click", + 0, + "Ignore Background Click", + "Clicks on the background do not start the stroke"); } /* Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) */ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) { - SculptSession *ss = CTX_data_active_object(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->sculpt; - if (ss) { - if (ss->layer_co) - MEM_freeN(ss->layer_co); - ss->layer_co = NULL; - } + if (ss) { + if (ss->layer_co) + MEM_freeN(ss->layer_co); + ss->layer_co = NULL; + } - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Persistent Base"; - ot->idname = "SCULPT_OT_set_persistent_base"; - ot->description = "Reset the copy of the mesh that is being sculpted on"; + /* identifiers */ + ot->name = "Set Persistent Base"; + ot->idname = "SCULPT_OT_set_persistent_base"; + ot->description = "Reset the copy of the mesh that is being sculpted on"; - /* api callbacks */ - ot->exec = sculpt_set_persistent_base_exec; - ot->poll = sculpt_mode_poll; + /* api callbacks */ + ot->exec = sculpt_set_persistent_base_exec; + ot->poll = sculpt_mode_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /************************** Dynamic Topology **************************/ static void sculpt_dynamic_topology_triangulate(BMesh *bm) { - if (bm->totloop != bm->totface * 3) { - BM_mesh_triangulate(bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); - } + if (bm->totloop != bm->totface * 3) { + BM_mesh_triangulate( + bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); + } } void sculpt_pbvh_clear(Object *ob) { - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->sculpt; - /* Clear out any existing DM and PBVH */ - if (ss->pbvh) { - BKE_pbvh_free(ss->pbvh); - } - ss->pbvh = NULL; - BKE_object_free_derived_caches(ob); + /* Clear out any existing DM and PBVH */ + if (ss->pbvh) { + BKE_pbvh_free(ss->pbvh); + } + ss->pbvh = NULL; + BKE_object_free_derived_caches(ob); } void sculpt_dyntopo_node_layers_add(SculptSession *ss) { - int cd_node_layer_index; + int cd_node_layer_index; - char layer_id[] = "_dyntopo_node_id"; + char layer_id[] = "_dyntopo_node_id"; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); - } + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); + if (cd_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); + } - ss->cd_vert_node_offset = CustomData_get_n_offset( - &ss->bm->vdata, CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); + ss->cd_vert_node_offset = CustomData_get_n_offset( + &ss->bm->vdata, + CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); - ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); - } + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); + if (cd_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); + } - ss->cd_face_node_offset = CustomData_get_n_offset( - &ss->bm->pdata, CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); + ss->cd_face_node_offset = CustomData_get_n_offset( + &ss->bm->pdata, + CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); - ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; } - -void sculpt_update_after_dynamic_topology_toggle( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +void sculpt_update_after_dynamic_topology_toggle(Depsgraph *depsgraph, Scene *scene, Object *ob) { - Sculpt *sd = scene->toolsettings->sculpt; + Sculpt *sd = scene->toolsettings->sculpt; - /* Create the PBVH */ - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, false); - WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); + /* Create the PBVH */ + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, false); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); } -void sculpt_dynamic_topology_enable_ex( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +void sculpt_dynamic_topology_enable_ex(Depsgraph *depsgraph, Scene *scene, Object *ob) { - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); - sculpt_pbvh_clear(ob); + sculpt_pbvh_clear(ob); - ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != 0; + ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != + 0; - /* Dynamic topology doesn't ensure selection state is valid, so remove [#36280] */ - BKE_mesh_mselect_clear(me); + /* Dynamic topology doesn't ensure selection state is valid, so remove [#36280] */ + BKE_mesh_mselect_clear(me); - /* Create triangles-only BMesh */ - ss->bm = BM_mesh_create( - &allocsize, - &((struct BMeshCreateParams){.use_toolflags = false,})); + /* Create triangles-only BMesh */ + ss->bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); - BM_mesh_bm_from_me( - ss->bm, me, (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, .use_shapekey = true, .active_shapekey = ob->shapenr, - })); - sculpt_dynamic_topology_triangulate(ss->bm); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - sculpt_dyntopo_node_layers_add(ss); - /* make sure the data for existing faces are initialized */ - if (me->totpoly != ss->bm->totface) { - BM_mesh_normals_update(ss->bm); - } + BM_mesh_bm_from_me(ss->bm, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); + sculpt_dynamic_topology_triangulate(ss->bm); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + sculpt_dyntopo_node_layers_add(ss); + /* make sure the data for existing faces are initialized */ + if (me->totpoly != ss->bm->totface) { + BM_mesh_normals_update(ss->bm); + } - /* Enable dynamic topology */ - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + /* Enable dynamic topology */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - /* Enable logging for undo/redo */ - ss->bm_log = BM_log_create(ss->bm); + /* Enable logging for undo/redo */ + ss->bm_log = BM_log_create(ss->bm); - /* Refresh */ - sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); + /* Refresh */ + sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); } /* Free the sculpt BMesh and BMLog * * If 'unode' is given, the BMesh's data is copied out to the unode * before the BMesh is deleted so that it can be restored from */ -void sculpt_dynamic_topology_disable_ex( - Depsgraph *depsgraph, - Scene *scene, Object *ob, SculptUndoNode *unode) -{ - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - - sculpt_pbvh_clear(ob); - - if (unode) { - /* Free all existing custom data */ - CustomData_free(&me->vdata, me->totvert); - CustomData_free(&me->edata, me->totedge); - CustomData_free(&me->fdata, me->totface); - CustomData_free(&me->ldata, me->totloop); - CustomData_free(&me->pdata, me->totpoly); - - /* Copy over stored custom data */ - me->totvert = unode->bm_enter_totvert; - me->totloop = unode->bm_enter_totloop; - me->totpoly = unode->bm_enter_totpoly; - me->totedge = unode->bm_enter_totedge; - me->totface = 0; - CustomData_copy(&unode->bm_enter_vdata, &me->vdata, CD_MASK_MESH.vmask, - CD_DUPLICATE, unode->bm_enter_totvert); - CustomData_copy(&unode->bm_enter_edata, &me->edata, CD_MASK_MESH.emask, - CD_DUPLICATE, unode->bm_enter_totedge); - CustomData_copy(&unode->bm_enter_ldata, &me->ldata, CD_MASK_MESH.lmask, - CD_DUPLICATE, unode->bm_enter_totloop); - CustomData_copy(&unode->bm_enter_pdata, &me->pdata, CD_MASK_MESH.pmask, - CD_DUPLICATE, unode->bm_enter_totpoly); - - BKE_mesh_update_customdata_pointers(me, false); - } - else { - BKE_sculptsession_bm_to_me(ob, true); - } - - /* Clear data */ - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - - /* typically valid but with global-undo they can be NULL, [#36234] */ - if (ss->bm) { - BM_mesh_free(ss->bm); - ss->bm = NULL; - } - if (ss->bm_log) { - BM_log_free(ss->bm_log); - ss->bm_log = NULL; - } - - BKE_particlesystem_reset_all(ob); - BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); - - /* Refresh */ - sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); +void sculpt_dynamic_topology_disable_ex(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + if (unode) { + /* Free all existing custom data */ + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + CustomData_free(&me->ldata, me->totloop); + CustomData_free(&me->pdata, me->totpoly); + + /* Copy over stored custom data */ + me->totvert = unode->bm_enter_totvert; + me->totloop = unode->bm_enter_totloop; + me->totpoly = unode->bm_enter_totpoly; + me->totedge = unode->bm_enter_totedge; + me->totface = 0; + CustomData_copy(&unode->bm_enter_vdata, + &me->vdata, + CD_MASK_MESH.vmask, + CD_DUPLICATE, + unode->bm_enter_totvert); + CustomData_copy(&unode->bm_enter_edata, + &me->edata, + CD_MASK_MESH.emask, + CD_DUPLICATE, + unode->bm_enter_totedge); + CustomData_copy(&unode->bm_enter_ldata, + &me->ldata, + CD_MASK_MESH.lmask, + CD_DUPLICATE, + unode->bm_enter_totloop); + CustomData_copy(&unode->bm_enter_pdata, + &me->pdata, + CD_MASK_MESH.pmask, + CD_DUPLICATE, + unode->bm_enter_totpoly); + + BKE_mesh_update_customdata_pointers(me, false); + } + else { + BKE_sculptsession_bm_to_me(ob, true); + } + + /* Clear data */ + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* typically valid but with global-undo they can be NULL, [#36234] */ + if (ss->bm) { + BM_mesh_free(ss->bm); + ss->bm = NULL; + } + if (ss->bm_log) { + BM_log_free(ss->bm_log); + ss->bm_log = NULL; + } + + BKE_particlesystem_reset_all(ob); + BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); + + /* Refresh */ + sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); } void sculpt_dynamic_topology_disable(bContext *C, SculptUndoNode *unode) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, unode); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, unode); } -static void sculpt_dynamic_topology_disable_with_undo( - Depsgraph *depsgraph, Scene *scene, Object *ob) +static void sculpt_dynamic_topology_disable_with_undo(Depsgraph *depsgraph, + Scene *scene, + Object *ob) { - SculptSession *ss = ob->sculpt; - if (ss->bm) { - sculpt_undo_push_begin("Dynamic topology disable"); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END); - sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, NULL); - sculpt_undo_push_end(); - } + SculptSession *ss = ob->sculpt; + if (ss->bm) { + sculpt_undo_push_begin("Dynamic topology disable"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END); + sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, NULL); + sculpt_undo_push_end(); + } } -static void sculpt_dynamic_topology_enable_with_undo( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +static void sculpt_dynamic_topology_enable_with_undo(Depsgraph *depsgraph, + Scene *scene, + Object *ob) { - SculptSession *ss = ob->sculpt; - if (ss->bm == NULL) { - sculpt_undo_push_begin("Dynamic topology enable"); - sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - sculpt_undo_push_end(); - } + SculptSession *ss = ob->sculpt; + if (ss->bm == NULL) { + sculpt_undo_push_begin("Dynamic topology enable"); + sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + sculpt_undo_push_end(); + } } static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; - WM_cursor_wait(1); + WM_cursor_wait(1); - if (ss->bm) { - sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); - } - else { - sculpt_dynamic_topology_enable_with_undo(depsgraph, scene, ob); - } + if (ss->bm) { + sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); + } + else { + sculpt_dynamic_topology_enable_with_undo(depsgraph, scene, ob); + } - WM_cursor_wait(0); + WM_cursor_wait(0); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } enum eDynTopoWarnFlag { - DYNTOPO_WARN_VDATA = (1 << 0), - DYNTOPO_WARN_EDATA = (1 << 1), - DYNTOPO_WARN_LDATA = (1 << 2), - DYNTOPO_WARN_MODIFIER = (1 << 3), + DYNTOPO_WARN_VDATA = (1 << 0), + DYNTOPO_WARN_EDATA = (1 << 1), + DYNTOPO_WARN_LDATA = (1 << 2), + DYNTOPO_WARN_MODIFIER = (1 << 3), }; static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); - uiLayout *layout = UI_popup_menu_layout(pup); + uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); + uiLayout *layout = UI_popup_menu_layout(pup); - if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { - const char *msg_error = TIP_("Vertex Data Detected!"); - const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); - uiItemL(layout, msg_error, ICON_INFO); - uiItemL(layout, msg, ICON_NONE); - uiItemS(layout); - } + if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { + const char *msg_error = TIP_("Vertex Data Detected!"); + const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } - if (flag & DYNTOPO_WARN_MODIFIER) { - const char *msg_error = TIP_("Generative Modifiers Detected!"); - const char *msg = TIP_("Keeping the modifiers will increase polycount when returning to object mode"); + if (flag & DYNTOPO_WARN_MODIFIER) { + const char *msg_error = TIP_("Generative Modifiers Detected!"); + const char *msg = TIP_( + "Keeping the modifiers will increase polycount when returning to object mode"); - uiItemL(layout, msg_error, ICON_INFO); - uiItemL(layout, msg, ICON_NONE); - uiItemS(layout); - } + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } - uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, NULL); + uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, NULL); - UI_popup_menu_end(C, pup); + UI_popup_menu_end(C, pup); - return OPERATOR_INTERFACE; + return OPERATOR_INTERFACE; } static enum eDynTopoWarnFlag sculpt_dynamic_topology_check(Scene *scene, Object *ob) { - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; - enum eDynTopoWarnFlag flag = 0; + enum eDynTopoWarnFlag flag = 0; - BLI_assert(ss->bm == NULL); - UNUSED_VARS_NDEBUG(ss); + BLI_assert(ss->bm == NULL); + UNUSED_VARS_NDEBUG(ss); - for (int i = 0; i < CD_NUMTYPES; i++) { - if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { - if (CustomData_has_layer(&me->vdata, i)) { - flag |= DYNTOPO_WARN_VDATA; - } - if (CustomData_has_layer(&me->edata, i)) { - flag |= DYNTOPO_WARN_EDATA; - } - if (CustomData_has_layer(&me->ldata, i)) { - flag |= DYNTOPO_WARN_LDATA; - } - } - } + for (int i = 0; i < CD_NUMTYPES; i++) { + if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { + if (CustomData_has_layer(&me->vdata, i)) { + flag |= DYNTOPO_WARN_VDATA; + } + if (CustomData_has_layer(&me->edata, i)) { + flag |= DYNTOPO_WARN_EDATA; + } + if (CustomData_has_layer(&me->ldata, i)) { + flag |= DYNTOPO_WARN_LDATA; + } + } + } - { - VirtualModifierData virtualModifierData; - ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + { + VirtualModifierData virtualModifierData; + ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); - /* exception for shape keys because we can edit those */ - for (; md; md = md->next) { - const ModifierTypeInfo *mti = modifierType_getInfo(md->type); - if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); + if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) + continue; - if (mti->type == eModifierTypeType_Constructive) { - flag |= DYNTOPO_WARN_MODIFIER; - break; - } - } - } + if (mti->type == eModifierTypeType_Constructive) { + flag |= DYNTOPO_WARN_MODIFIER; + break; + } + } + } - return flag; + return flag; } -static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int sculpt_dynamic_topology_toggle_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; - if (!ss->bm) { - Scene *scene = CTX_data_scene(C); - enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); + if (!ss->bm) { + Scene *scene = CTX_data_scene(C); + enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); - if (flag) { - /* The mesh has customdata that will be lost, let the user confirm this is OK */ - return dyntopo_warning_popup(C, op->type, flag); - } - } + if (flag) { + /* The mesh has customdata that will be lost, let the user confirm this is OK */ + return dyntopo_warning_popup(C, op->type, flag); + } + } - return sculpt_dynamic_topology_toggle_exec(C, op); + return sculpt_dynamic_topology_toggle_exec(C, op); } static void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Dynamic Topology Toggle"; - ot->idname = "SCULPT_OT_dynamic_topology_toggle"; - ot->description = "Dynamic topology alters the mesh topology while sculpting"; + /* identifiers */ + ot->name = "Dynamic Topology Toggle"; + ot->idname = "SCULPT_OT_dynamic_topology_toggle"; + ot->description = "Dynamic topology alters the mesh topology while sculpting"; - /* api callbacks */ - ot->invoke = sculpt_dynamic_topology_toggle_invoke; - ot->exec = sculpt_dynamic_topology_toggle_exec; - ot->poll = sculpt_mode_poll; + /* api callbacks */ + ot->invoke = sculpt_dynamic_topology_toggle_invoke; + ot->exec = sculpt_dynamic_topology_toggle_exec; + ot->poll = sculpt_mode_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /************************* SCULPT_OT_optimize *************************/ static int sculpt_optimize_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - sculpt_pbvh_clear(ob); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + sculpt_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static bool sculpt_and_dynamic_topology_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return sculpt_mode_poll(C) && ob->sculpt->bm; + return sculpt_mode_poll(C) && ob->sculpt->bm; } /* The BVH gets less optimal more quickly with dynamic topology than @@ -5777,572 +5831,578 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) * to recalculate it than toggling modes. */ static void SCULPT_OT_optimize(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Optimize"; - ot->idname = "SCULPT_OT_optimize"; - ot->description = "Recalculate the sculpt BVH to improve performance"; + /* identifiers */ + ot->name = "Optimize"; + ot->idname = "SCULPT_OT_optimize"; + ot->description = "Recalculate the sculpt BVH to improve performance"; - /* api callbacks */ - ot->exec = sculpt_optimize_exec; - ot->poll = sculpt_and_dynamic_topology_poll; + /* api callbacks */ + ot->exec = sculpt_optimize_exec; + ot->poll = sculpt_and_dynamic_topology_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /********************* Dynamic topology symmetrize ********************/ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); - const Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = ob->sculpt; + Object *ob = CTX_data_active_object(C); + const Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; - /* To simplify undo for symmetrize, all BMesh elements are logged - * as deleted, then after symmetrize operation all BMesh elements - * are logged as added (as opposed to attempting to store just the - * parts that symmetrize modifies) */ - sculpt_undo_push_begin("Dynamic topology symmetrize"); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); - BM_log_before_all_removed(ss->bm, ss->bm_log); + /* To simplify undo for symmetrize, all BMesh elements are logged + * as deleted, then after symmetrize operation all BMesh elements + * are logged as added (as opposed to attempting to store just the + * parts that symmetrize modifies) */ + sculpt_undo_push_begin("Dynamic topology symmetrize"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); + BM_log_before_all_removed(ss->bm, ss->bm_log); - BM_mesh_toolflags_set(ss->bm, true); + BM_mesh_toolflags_set(ss->bm, true); - /* Symmetrize and re-triangulate */ - BMO_op_callf(ss->bm, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "symmetrize input=%avef direction=%i dist=%f", - sd->symmetrize_direction, 0.00001f); - sculpt_dynamic_topology_triangulate(ss->bm); + /* Symmetrize and re-triangulate */ + BMO_op_callf(ss->bm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "symmetrize input=%avef direction=%i dist=%f", + sd->symmetrize_direction, + 0.00001f); + sculpt_dynamic_topology_triangulate(ss->bm); - /* bisect operator flags edges (keep tags clean for edge queue) */ - BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); + /* bisect operator flags edges (keep tags clean for edge queue) */ + BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_toolflags_set(ss->bm, false); + BM_mesh_toolflags_set(ss->bm, false); - /* Finish undo */ - BM_log_all_added(ss->bm, ss->bm_log); - sculpt_undo_push_end(); + /* Finish undo */ + BM_log_all_added(ss->bm, ss->bm_log); + sculpt_undo_push_end(); - /* Redraw */ - sculpt_pbvh_clear(ob); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + /* Redraw */ + sculpt_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_symmetrize(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Symmetrize"; - ot->idname = "SCULPT_OT_symmetrize"; - ot->description = "Symmetrize the topology modifications"; + /* identifiers */ + ot->name = "Symmetrize"; + ot->idname = "SCULPT_OT_symmetrize"; + ot->description = "Symmetrize the topology modifications"; - /* api callbacks */ - ot->exec = sculpt_symmetrize_exec; - ot->poll = sculpt_and_dynamic_topology_poll; + /* api callbacks */ + ot->exec = sculpt_symmetrize_exec; + ot->poll = sculpt_and_dynamic_topology_poll; } /**** Toggle operator for turning sculpt mode on or off ****/ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob) { - /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); - - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); - ob->sculpt->mode_type = OB_MODE_SCULPT; - BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); -} - -static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, Object *ob, MultiresModifierData *mmd) -{ - int flush_recalc = 0; - /* multires in sculpt mode could have different from object mode subdivision level */ - flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; - /* if object has got active modifiers, it's dm could be different in sculpt mode */ - flush_recalc |= sculpt_has_active_modifiers(scene, ob); - return flush_recalc; -} - -void ED_object_sculptmode_enter_ex( - Main *bmain, Depsgraph *depsgraph, - Scene *scene, Object *ob, const bool force_dyntopo, - ReportList *reports) -{ - const int mode_flag = OB_MODE_SCULPT; - Mesh *me = BKE_mesh_from_object(ob); - - /* Enter sculptmode */ - ob->mode |= mode_flag; - - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - - const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); - - if (flush_recalc) - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* Create sculpt mode session data */ - if (ob->sculpt) { - BKE_sculptsession_free(ob); - } - - /* Make sure derived final from original object does not reference possibly - * freed memory. - */ - BKE_object_free_derived_caches(ob); - - sculpt_init_session(depsgraph, scene, ob); - - /* Mask layer is required */ - if (mmd) { - /* XXX, we could attempt to support adding mask data mid-sculpt mode (with multi-res) - * but this ends up being quite tricky (and slow) */ - BKE_sculpt_mask_layers_ensure(ob, mmd); - } - - if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { - BKE_report(reports, RPT_WARNING, - "Object has non-uniform scale, sculpting may be unpredictable"); - } - else if (is_negative_m4(ob->obmat)) { - BKE_report(reports, RPT_WARNING, - "Object has negative scale, sculpting may be unpredictable"); - } - - Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); - BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); - - paint_cursor_start_explicit(paint, bmain->wm.first, sculpt_poll_view3d); - - /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, - * As long as no data was added that is not supported. */ - if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { - const char *message_unsupported = NULL; - if (me->totloop != me->totpoly * 3) { - message_unsupported = TIP_("non-triangle face"); - } - else if (mmd != NULL) { - message_unsupported = TIP_("multi-res modifier"); - } - else { - enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); - if (flag == 0) { - /* pass */ - } - else if (flag & DYNTOPO_WARN_VDATA) { - message_unsupported = TIP_("vertex data"); - } - else if (flag & DYNTOPO_WARN_EDATA) { - message_unsupported = TIP_("edge data"); - } - else if (flag & DYNTOPO_WARN_LDATA) { - message_unsupported = TIP_("face data"); - } - else if (flag & DYNTOPO_WARN_MODIFIER) { - message_unsupported = TIP_("constructive modifier"); - } - else { - BLI_assert(0); - } - } - - if ((message_unsupported == NULL) || force_dyntopo) { - /* Needed because we may be entering this mode before the undo system loads. */ - wmWindowManager *wm = bmain->wm.first; - bool has_undo = wm->undo_stack != NULL; - /* undo push is needed to prevent memory leak */ - if (has_undo) { - sculpt_undo_push_begin("Dynamic topology enable"); - } - sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); - if (has_undo) { - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - sculpt_undo_push_end(); - } - } - else { - BKE_reportf(reports, RPT_WARNING, - "Dynamic Topology found: %s, disabled", - message_unsupported); - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - } - } - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + /* Create persistent sculpt mode data */ + BKE_sculpt_toolsettings_data_ensure(scene); + + ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->mode_type = OB_MODE_SCULPT; + BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); +} + +static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, + Object *ob, + MultiresModifierData *mmd) +{ + int flush_recalc = 0; + /* multires in sculpt mode could have different from object mode subdivision level */ + flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; + /* if object has got active modifiers, it's dm could be different in sculpt mode */ + flush_recalc |= sculpt_has_active_modifiers(scene, ob); + return flush_recalc; +} + +void ED_object_sculptmode_enter_ex(Main *bmain, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + const bool force_dyntopo, + ReportList *reports) +{ + const int mode_flag = OB_MODE_SCULPT; + Mesh *me = BKE_mesh_from_object(ob); + + /* Enter sculptmode */ + ob->mode |= mode_flag; + + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + + const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); + + if (flush_recalc) + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* Create sculpt mode session data */ + if (ob->sculpt) { + BKE_sculptsession_free(ob); + } + + /* Make sure derived final from original object does not reference possibly + * freed memory. + */ + BKE_object_free_derived_caches(ob); + + sculpt_init_session(depsgraph, scene, ob); + + /* Mask layer is required */ + if (mmd) { + /* XXX, we could attempt to support adding mask data mid-sculpt mode (with multi-res) + * but this ends up being quite tricky (and slow) */ + BKE_sculpt_mask_layers_ensure(ob, mmd); + } + + if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && + fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { + BKE_report( + reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable"); + } + else if (is_negative_m4(ob->obmat)) { + BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable"); + } + + Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); + BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); + + paint_cursor_start_explicit(paint, bmain->wm.first, sculpt_poll_view3d); + + /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, + * As long as no data was added that is not supported. */ + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + const char *message_unsupported = NULL; + if (me->totloop != me->totpoly * 3) { + message_unsupported = TIP_("non-triangle face"); + } + else if (mmd != NULL) { + message_unsupported = TIP_("multi-res modifier"); + } + else { + enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); + if (flag == 0) { + /* pass */ + } + else if (flag & DYNTOPO_WARN_VDATA) { + message_unsupported = TIP_("vertex data"); + } + else if (flag & DYNTOPO_WARN_EDATA) { + message_unsupported = TIP_("edge data"); + } + else if (flag & DYNTOPO_WARN_LDATA) { + message_unsupported = TIP_("face data"); + } + else if (flag & DYNTOPO_WARN_MODIFIER) { + message_unsupported = TIP_("constructive modifier"); + } + else { + BLI_assert(0); + } + } + + if ((message_unsupported == NULL) || force_dyntopo) { + /* Needed because we may be entering this mode before the undo system loads. */ + wmWindowManager *wm = bmain->wm.first; + bool has_undo = wm->undo_stack != NULL; + /* undo push is needed to prevent memory leak */ + if (has_undo) { + sculpt_undo_push_begin("Dynamic topology enable"); + } + sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); + if (has_undo) { + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + sculpt_undo_push_end(); + } + } + else { + BKE_reportf( + reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported); + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + } + } + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_sculptmode_enter(struct bContext *C, ReportList *reports) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); } -void ED_object_sculptmode_exit_ex( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +void ED_object_sculptmode_exit_ex(Depsgraph *depsgraph, Scene *scene, Object *ob) { - const int mode_flag = OB_MODE_SCULPT; - Mesh *me = BKE_mesh_from_object(ob); + const int mode_flag = OB_MODE_SCULPT; + Mesh *me = BKE_mesh_from_object(ob); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - if (mmd) { - multires_force_update(ob); - } + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + if (mmd) { + multires_force_update(ob); + } - /* Not needed for now. */ + /* Not needed for now. */ #if 0 - const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); + const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); #endif - /* Always for now, so leaving sculpt mode always ensures scene is in - * a consistent state. - */ - if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } + /* Always for now, so leaving sculpt mode always ensures scene is in + * a consistent state. + */ + if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } - if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { - /* Dynamic topology must be disabled before exiting sculpt - * mode to ensure the undo stack stays in a consistent - * state */ - sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + /* Dynamic topology must be disabled before exiting sculpt + * mode to ensure the undo stack stays in a consistent + * state */ + sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); - /* store so we know to re-enable when entering sculpt mode */ - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - } + /* store so we know to re-enable when entering sculpt mode */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + } - /* Leave sculptmode */ - ob->mode &= ~mode_flag; + /* Leave sculptmode */ + ob->mode &= ~mode_flag; - BKE_sculptsession_free(ob); + BKE_sculptsession_free(ob); - paint_cursor_delete_textures(); + paint_cursor_delete_textures(); - /* Never leave derived meshes behind. */ - BKE_object_free_derived_caches(ob); + /* Never leave derived meshes behind. */ + BKE_object_free_derived_caches(ob); - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_sculptmode_exit(bContext *C) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - ED_object_sculptmode_exit_ex(depsgraph, scene, ob); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + ED_object_sculptmode_exit_ex(depsgraph, scene, ob); } static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) { - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - const int mode_flag = OB_MODE_SCULPT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + const int mode_flag = OB_MODE_SCULPT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } - if (is_mode_set) { - ED_object_sculptmode_exit_ex(depsgraph, scene, ob); - } - else { - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); - BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); - } + if (is_mode_set) { + ED_object_sculptmode_exit_ex(depsgraph, scene, ob); + } + else { + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); + BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); + } - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - WM_toolsystem_update_from_context_view3d(C); + WM_toolsystem_update_from_context_view3d(C); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sculpt Mode"; - ot->idname = "SCULPT_OT_sculptmode_toggle"; - ot->description = "Toggle sculpt mode in 3D view"; + /* identifiers */ + ot->name = "Sculpt Mode"; + ot->idname = "SCULPT_OT_sculptmode_toggle"; + ot->description = "Toggle sculpt mode in 3D view"; - /* api callbacks */ - ot->exec = sculpt_mode_toggle_exec; - ot->poll = ED_operator_object_active_editable_mesh; + /* api callbacks */ + ot->exec = sculpt_mode_toggle_exec; + ot->poll = ED_operator_object_active_editable_mesh; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } static bool sculpt_and_constant_or_manual_detail_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - return sculpt_mode_poll(C) && ob->sculpt->bm && - (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); + return sculpt_mode_poll(C) && ob->sculpt->bm && + (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); } static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - float size; - float bb_min[3], bb_max[3], center[3], dim[3]; - int i, totnodes; - PBVHNode **nodes; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + float size; + float bb_min[3], bb_max[3], center[3], dim[3]; + int i, totnodes; + PBVHNode **nodes; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); - if (!totnodes) - return OPERATOR_CANCELLED; + if (!totnodes) + return OPERATOR_CANCELLED; - for (i = 0; i < totnodes; i++) { - BKE_pbvh_node_mark_topology_update(nodes[i]); - } - /* get the bounding box, it's center and size */ - BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); - add_v3_v3v3(center, bb_min, bb_max); - mul_v3_fl(center, 0.5f); - sub_v3_v3v3(dim, bb_max, bb_min); - size = max_fff(dim[0], dim[1], dim[2]); + for (i = 0; i < totnodes; i++) { + BKE_pbvh_node_mark_topology_update(nodes[i]); + } + /* get the bounding box, it's center and size */ + BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); + add_v3_v3v3(center, bb_min, bb_max); + mul_v3_fl(center, 0.5f); + sub_v3_v3v3(dim, bb_max, bb_min); + size = max_fff(dim[0], dim[1], dim[2]); - /* update topology size */ - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + /* update topology size */ + float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); - sculpt_undo_push_begin("Dynamic topology flood fill"); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); + sculpt_undo_push_begin("Dynamic topology flood fill"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); - while (BKE_pbvh_bmesh_update_topology( - ss->pbvh, PBVH_Collapse | PBVH_Subdivide, - center, NULL, size, false, false)) - { - for (i = 0; i < totnodes; i++) - BKE_pbvh_node_mark_topology_update(nodes[i]); - } + while (BKE_pbvh_bmesh_update_topology( + ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false)) { + for (i = 0; i < totnodes; i++) + BKE_pbvh_node_mark_topology_update(nodes[i]); + } - MEM_freeN(nodes); - sculpt_undo_push_end(); + MEM_freeN(nodes); + sculpt_undo_push_end(); - /* force rebuild of pbvh for better BB placement */ - sculpt_pbvh_clear(ob); - /* Redraw */ - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + /* force rebuild of pbvh for better BB placement */ + sculpt_pbvh_clear(ob); + /* Redraw */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Detail Flood Fill"; - ot->idname = "SCULPT_OT_detail_flood_fill"; - ot->description = "Flood fill the mesh with the selected detail setting"; + /* identifiers */ + ot->name = "Detail Flood Fill"; + ot->idname = "SCULPT_OT_detail_flood_fill"; + ot->description = "Flood fill the mesh with the selected detail setting"; - /* api callbacks */ - ot->exec = sculpt_detail_flood_fill_exec; - ot->poll = sculpt_and_constant_or_manual_detail_poll; + /* api callbacks */ + ot->exec = sculpt_detail_flood_fill_exec; + ot->poll = sculpt_and_constant_or_manual_detail_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static void sample_detail(bContext *C, int mx, int my) { - /* Find 3D view to pick from. */ - bScreen *screen = CTX_wm_screen(C); - ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); - ARegion *ar = (sa) ? BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my) : NULL; - if (ar == NULL) { - return; - } + /* Find 3D view to pick from. */ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); + ARegion *ar = (sa) ? BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my) : NULL; + if (ar == NULL) { + return; + } - /* Set context to 3D view. */ - ScrArea *prev_sa = CTX_wm_area(C); - ARegion *prev_ar = CTX_wm_region(C); - CTX_wm_area_set(C, sa); - CTX_wm_region_set(C, ar); + /* Set context to 3D view. */ + ScrArea *prev_sa = CTX_wm_area(C); + ARegion *prev_ar = CTX_wm_region(C); + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); - /* Pick sample detail. */ - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = vc.obact; - Brush *brush = BKE_paint_brush(&sd->paint); + /* Pick sample detail. */ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = vc.obact; + Brush *brush = BKE_paint_brush(&sd->paint); - sculpt_stroke_modifiers_check(C, ob, brush); + sculpt_stroke_modifiers_check(C, ob, brush); - float mouse[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; - float ray_start[3], ray_end[3], ray_normal[3]; - float depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, false); + float mouse[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; + float ray_start[3], ray_end[3], ray_normal[3]; + float depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, false); - SculptDetailRaycastData srd; - srd.hit = 0; - srd.ray_start = ray_start; - srd.ray_normal = ray_normal; - srd.depth = depth; - srd.edge_length = 0.0f; + SculptDetailRaycastData srd; + srd.hit = 0; + srd.ray_start = ray_start; + srd.ray_normal = ray_normal; + srd.depth = depth; + srd.edge_length = 0.0f; - BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, - ray_start, ray_normal, false); + BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false); - if (srd.hit && srd.edge_length > 0.0f) { - /* Convert edge length to world space detail resolution. */ - sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); - } + if (srd.hit && srd.edge_length > 0.0f) { + /* Convert edge length to world space detail resolution. */ + sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); + } - /* Restore context. */ - CTX_wm_area_set(C, prev_sa); - CTX_wm_region_set(C, prev_ar); + /* Restore context. */ + CTX_wm_area_set(C, prev_sa); + CTX_wm_region_set(C, prev_ar); } static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) { - int ss_co[2]; - RNA_int_get_array(op->ptr, "location", ss_co); - sample_detail(C, ss_co[0], ss_co[1]); - return OPERATOR_FINISHED; + int ss_co[2]; + RNA_int_get_array(op->ptr, "location", ss_co); + sample_detail(C, ss_co[0], ss_co[1]); + return OPERATOR_FINISHED; } - static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) { - ED_workspace_status_text(C, "Click on the mesh to set the detail"); - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + ED_workspace_status_text(C, "Click on the mesh to set the detail"); + WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wmEvent *event) { - switch (event->type) { - case LEFTMOUSE: - if (event->val == KM_PRESS) { - int ss_co[2] = {event->x, event->y}; + switch (event->type) { + case LEFTMOUSE: + if (event->val == KM_PRESS) { + int ss_co[2] = {event->x, event->y}; - sample_detail(C, ss_co[0], ss_co[1]); + sample_detail(C, ss_co[0], ss_co[1]); - RNA_int_set_array(op->ptr, "location", ss_co); - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_int_set_array(op->ptr, "location", ss_co); + WM_cursor_modal_restore(CTX_wm_window(C)); + ED_workspace_status_text(C, NULL); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - return OPERATOR_FINISHED; - } - break; + return OPERATOR_FINISHED; + } + break; - case RIGHTMOUSE: - { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); + case RIGHTMOUSE: { + WM_cursor_modal_restore(CTX_wm_window(C)); + ED_workspace_status_text(C, NULL); - return OPERATOR_CANCELLED; - } - } + return OPERATOR_CANCELLED; + } + } - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } - static void SCULPT_OT_sample_detail_size(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sample Detail Size"; - ot->idname = "SCULPT_OT_sample_detail_size"; - ot->description = "Sample the mesh detail on clicked point"; + /* identifiers */ + ot->name = "Sample Detail Size"; + ot->idname = "SCULPT_OT_sample_detail_size"; + ot->description = "Sample the mesh detail on clicked point"; - /* api callbacks */ - ot->invoke = sculpt_sample_detail_size_invoke; - ot->exec = sculpt_sample_detail_size_exec; - ot->modal = sculpt_sample_detail_size_modal; - ot->poll = sculpt_and_constant_or_manual_detail_poll; + /* api callbacks */ + ot->invoke = sculpt_sample_detail_size_invoke; + ot->exec = sculpt_sample_detail_size_exec; + ot->modal = sculpt_sample_detail_size_modal; + ot->poll = sculpt_and_constant_or_manual_detail_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int_array(ot->srna, "location", 2, NULL, 0, SHRT_MAX, - "Location", "Screen Coordinates of sampling", 0, SHRT_MAX); + RNA_def_int_array(ot->srna, + "location", + 2, + NULL, + 0, + SHRT_MAX, + "Location", + "Screen Coordinates of sampling", + 0, + SHRT_MAX); } - /* Dynamic-topology detail size * * This should be improved further, perhaps by showing a triangle * grid rather than brush alpha */ static void set_brush_rc_props(PointerRNA *ptr, const char *prop) { - char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); - RNA_string_set(ptr, "data_path_primary", path); - MEM_freeN(path); + char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); + RNA_string_set(ptr, "data_path_primary", path); + MEM_freeN(path); } static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - PointerRNA props_ptr; - wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); - WM_operator_properties_create_ptr(&props_ptr, ot); + WM_operator_properties_create_ptr(&props_ptr, ot); - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); - } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); - } - else { - set_brush_rc_props(&props_ptr, "detail_size"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); - } + if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { + set_brush_rc_props(&props_ptr, "constant_detail_resolution"); + RNA_string_set( + &props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); + } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + set_brush_rc_props(&props_ptr, "constant_detail_resolution"); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); + } + else { + set_brush_rc_props(&props_ptr, "detail_size"); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); + } - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); - WM_operator_properties_free(&props_ptr); + WM_operator_properties_free(&props_ptr); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_set_detail_size(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Detail Size"; - ot->idname = "SCULPT_OT_set_detail_size"; - ot->description = "Set the mesh detail (either relative or constant one, depending on current dyntopo mode)"; + /* identifiers */ + ot->name = "Set Detail Size"; + ot->idname = "SCULPT_OT_set_detail_size"; + ot->description = + "Set the mesh detail (either relative or constant one, depending on current dyntopo mode)"; - /* api callbacks */ - ot->exec = sculpt_set_detail_size_exec; - ot->poll = sculpt_and_dynamic_topology_poll; + /* api callbacks */ + ot->exec = sculpt_set_detail_size_exec; + ot->poll = sculpt_and_dynamic_topology_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } void ED_operatortypes_sculpt(void) { - WM_operatortype_append(SCULPT_OT_brush_stroke); - WM_operatortype_append(SCULPT_OT_sculptmode_toggle); - WM_operatortype_append(SCULPT_OT_set_persistent_base); - WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); - WM_operatortype_append(SCULPT_OT_optimize); - WM_operatortype_append(SCULPT_OT_symmetrize); - WM_operatortype_append(SCULPT_OT_detail_flood_fill); - WM_operatortype_append(SCULPT_OT_sample_detail_size); - WM_operatortype_append(SCULPT_OT_set_detail_size); + WM_operatortype_append(SCULPT_OT_brush_stroke); + WM_operatortype_append(SCULPT_OT_sculptmode_toggle); + WM_operatortype_append(SCULPT_OT_set_persistent_base); + WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); + WM_operatortype_append(SCULPT_OT_optimize); + WM_operatortype_append(SCULPT_OT_symmetrize); + WM_operatortype_append(SCULPT_OT_detail_flood_fill); + WM_operatortype_append(SCULPT_OT_sample_detail_size); + WM_operatortype_append(SCULPT_OT_set_detail_size); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 44ed680a916..97f2ca1a143 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -21,7 +21,6 @@ * \ingroup edsculpt */ - #ifndef __SCULPT_INTERN_H__ #define __SCULPT_INTERN_H__ @@ -52,72 +51,72 @@ bool sculpt_stroke_get_location(struct bContext *C, float out[3], const float mo /* Dynamic topology */ void sculpt_pbvh_clear(Object *ob); void sculpt_dyntopo_node_layers_add(struct SculptSession *ss); -void sculpt_update_after_dynamic_topology_toggle( - struct Depsgraph *depsgraph, - struct Scene *scene, struct Object *ob); -void sculpt_dynamic_topology_enable_ex( - struct Depsgraph *depsgraph, - struct Scene *scene, struct Object *ob); - -void sculpt_dynamic_topology_disable_ex( - struct Depsgraph *depsgraph, - struct Scene *scene, struct Object *ob, - struct SculptUndoNode *unode); +void sculpt_update_after_dynamic_topology_toggle(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob); +void sculpt_dynamic_topology_enable_ex(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob); + +void sculpt_dynamic_topology_disable_ex(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct SculptUndoNode *unode); void sculpt_dynamic_topology_disable(bContext *C, struct SculptUndoNode *unode); /* Undo */ typedef enum { - SCULPT_UNDO_COORDS, - SCULPT_UNDO_HIDDEN, - SCULPT_UNDO_MASK, - SCULPT_UNDO_DYNTOPO_BEGIN, - SCULPT_UNDO_DYNTOPO_END, - SCULPT_UNDO_DYNTOPO_SYMMETRIZE, + SCULPT_UNDO_COORDS, + SCULPT_UNDO_HIDDEN, + SCULPT_UNDO_MASK, + SCULPT_UNDO_DYNTOPO_BEGIN, + SCULPT_UNDO_DYNTOPO_END, + SCULPT_UNDO_DYNTOPO_SYMMETRIZE, } SculptUndoType; typedef struct SculptUndoNode { - struct SculptUndoNode *next, *prev; - - SculptUndoType type; - - char idname[MAX_ID_NAME]; /* name instead of pointer*/ - void *node; /* only during push, not valid afterwards! */ - - float (*co)[3]; - float (*orig_co)[3]; - short (*no)[3]; - float *mask; - int totvert; - - /* non-multires */ - int maxvert; /* to verify if totvert it still the same */ - int *index; /* to restore into right location */ - BLI_bitmap *vert_hidden; - - /* multires */ - int maxgrid; /* same for grid */ - int gridsize; /* same for grid */ - int totgrid; /* to restore into right location */ - int *grids; /* to restore into right location */ - BLI_bitmap **grid_hidden; - - /* bmesh */ - struct BMLogEntry *bm_entry; - bool applied; - CustomData bm_enter_vdata; - CustomData bm_enter_edata; - CustomData bm_enter_ldata; - CustomData bm_enter_pdata; - int bm_enter_totvert; - int bm_enter_totedge; - int bm_enter_totloop; - int bm_enter_totpoly; - - /* shape keys */ - char shapeName[sizeof(((KeyBlock *)0))->name]; - - size_t undo_size; + struct SculptUndoNode *next, *prev; + + SculptUndoType type; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ + void *node; /* only during push, not valid afterwards! */ + + float (*co)[3]; + float (*orig_co)[3]; + short (*no)[3]; + float *mask; + int totvert; + + /* non-multires */ + int maxvert; /* to verify if totvert it still the same */ + int *index; /* to restore into right location */ + BLI_bitmap *vert_hidden; + + /* multires */ + int maxgrid; /* same for grid */ + int gridsize; /* same for grid */ + int totgrid; /* to restore into right location */ + int *grids; /* to restore into right location */ + BLI_bitmap **grid_hidden; + + /* bmesh */ + struct BMLogEntry *bm_entry; + bool applied; + CustomData bm_enter_vdata; + CustomData bm_enter_edata; + CustomData bm_enter_ldata; + CustomData bm_enter_pdata; + int bm_enter_totvert; + int bm_enter_totedge; + int bm_enter_totloop; + int bm_enter_totpoly; + + /* shape keys */ + char shapeName[sizeof(((KeyBlock *)0))->name]; + + size_t undo_size; } SculptUndoNode; /* Factor of brush to have rake point following behind @@ -125,89 +124,88 @@ typedef struct SculptUndoNode { #define SCULPT_RAKE_BRUSH_FACTOR 0.25f struct SculptRakeData { - float follow_dist; - float follow_co[3]; + float follow_dist; + float follow_co[3]; }; /* Single struct used by all BLI_task threaded callbacks, let's avoid adding 10's of those... */ typedef struct SculptThreadedTaskData { - struct bContext *C; - struct Sculpt *sd; - struct Object *ob; - const struct Brush *brush; - struct PBVHNode **nodes; - int totnode; - - struct VPaint *vp; - struct VPaintData *vpd; - struct WPaintData *wpd; - struct WeightPaintInfo *wpi; - unsigned int *lcol; - struct Mesh *me; - /* For passing generic params. */ - void *custom_data; - - - /* Data specific to some callbacks. */ - /* Note: even if only one or two of those are used at a time, keeping them separated, names help figuring out - * what it is, and memory overhead is ridiculous anyway... */ - float flippedbstrength; - float angle; - float strength; - bool smooth_mask; - bool has_bm_orco; - - struct SculptProjectVector *spvc; - float *offset; - float *grab_delta; - float *cono; - float *area_no; - float *area_no_sp; - float *area_co; - float(*mat)[4]; - float(*vertCos)[3]; - - /* 0=towards view, 1=flipped */ - float(*area_cos)[3]; - float(*area_nos)[3]; - int *count; - - ThreadMutex mutex; + struct bContext *C; + struct Sculpt *sd; + struct Object *ob; + const struct Brush *brush; + struct PBVHNode **nodes; + int totnode; + + struct VPaint *vp; + struct VPaintData *vpd; + struct WPaintData *wpd; + struct WeightPaintInfo *wpi; + unsigned int *lcol; + struct Mesh *me; + /* For passing generic params. */ + void *custom_data; + + /* Data specific to some callbacks. */ + /* Note: even if only one or two of those are used at a time, keeping them separated, names help figuring out + * what it is, and memory overhead is ridiculous anyway... */ + float flippedbstrength; + float angle; + float strength; + bool smooth_mask; + bool has_bm_orco; + + struct SculptProjectVector *spvc; + float *offset; + float *grab_delta; + float *cono; + float *area_no; + float *area_no_sp; + float *area_co; + float (*mat)[4]; + float (*vertCos)[3]; + + /* 0=towards view, 1=flipped */ + float (*area_cos)[3]; + float (*area_nos)[3]; + int *count; + + ThreadMutex mutex; } SculptThreadedTaskData; /*************** Brush testing declarations ****************/ typedef struct SculptBrushTest { - float radius_squared; - float location[3]; - float dist; - int mirror_symmetry_pass; + float radius_squared; + float location[3]; + float dist; + int mirror_symmetry_pass; - /* For circle (not sphere) projection. */ - float plane_view[4]; + /* For circle (not sphere) projection. */ + float plane_view[4]; - /* Some tool code uses a plane for it's calculateions. */ - float plane_tool[4]; + /* Some tool code uses a plane for it's calculateions. */ + float plane_tool[4]; - /* View3d clipping - only set rv3d for clipping */ - struct RegionView3D *clip_rv3d; + /* View3d clipping - only set rv3d for clipping */ + struct RegionView3D *clip_rv3d; } SculptBrushTest; typedef bool (*SculptBrushTestFn)(SculptBrushTest *test, const float co[3]); typedef struct { - struct Sculpt *sd; - struct SculptSession *ss; - float radius_squared; - bool original; + struct Sculpt *sd; + struct SculptSession *ss; + float radius_squared; + bool original; } SculptSearchSphereData; typedef struct { - struct Sculpt *sd; - struct SculptSession *ss; - float radius_squared; - bool original; - struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc; + struct Sculpt *sd; + struct SculptSession *ss; + float radius_squared; + bool original; + struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc; } SculptSearchCircleData; void sculpt_brush_test_init(struct SculptSession *ss, SculptBrushTest *test); @@ -219,26 +217,28 @@ bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]); bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v); bool sculpt_search_circle_cb(PBVHNode *node, void *data_v); -SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape( - SculptSession *ss, SculptBrushTest *test, char falloff_shape); -const float *sculpt_brush_frontface_normal_from_falloff_shape( - SculptSession *ss, char falloff_shape); - -float tex_strength( - struct SculptSession *ss, const struct Brush *br, - const float point[3], - const float len, - const short vno[3], - const float fno[3], - const float mask, - const int thread_id); +SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(SculptSession *ss, + SculptBrushTest *test, + char falloff_shape); +const float *sculpt_brush_frontface_normal_from_falloff_shape(SculptSession *ss, + char falloff_shape); + +float tex_strength(struct SculptSession *ss, + const struct Brush *br, + const float point[3], + const float len, + const short vno[3], + const float fno[3], + const float mask, + const int thread_id); /* just for vertex paint. */ -void sculpt_pbvh_calc_area_normal( - const struct Brush *brush, Object *ob, - PBVHNode **nodes, int totnode, - bool use_threading, - float r_area_no[3]); +void sculpt_pbvh_calc_area_normal(const struct Brush *brush, + Object *ob, + PBVHNode **nodes, + int totnode, + bool use_threading, + float r_area_no[3]); /* Cache stroke properties. Used because * RNA property lookup isn't particularly fast. @@ -247,98 +247,99 @@ void sculpt_pbvh_calc_area_normal( */ typedef struct StrokeCache { - /* Invariants */ - float initial_radius; - float scale[3]; - int flag; - float clip_tolerance[3]; - float initial_mouse[2]; - - /* Variants */ - float radius; - float radius_squared; - float true_location[3]; - float true_last_location[3]; - float location[3]; - float last_location[3]; - bool is_last_valid; - - bool pen_flip; - bool invert; - float pressure; - float mouse[2]; - float bstrength; - float normal_weight; /* from brush (with optional override) */ - - /* The rest is temporary storage that isn't saved as a property */ - - bool first_time; /* Beginning of stroke may do some things special */ - - /* from ED_view3d_ob_project_mat_get() */ - float projection_mat[4][4]; - - /* Clean this up! */ - struct ViewContext *vc; - const struct Brush *brush; - - float special_rotation; - float grab_delta[3], grab_delta_symmetry[3]; - float old_grab_location[3], orig_grab_location[3]; - - /* screen-space rotation defined by mouse motion */ - float rake_rotation[4], rake_rotation_symmetry[4]; - bool is_rake_rotation_valid; - struct SculptRakeData rake_data; - - /* Symmetry index between 0 and 7 bit combo 0 is Brush only; - * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - int symmetry; - int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/ - float true_view_normal[3]; - float view_normal[3]; - - /* sculpt_normal gets calculated by calc_sculpt_normal(), then the - * sculpt_normal_symm gets updated quickly with the usual symmetry - * transforms */ - float sculpt_normal[3]; - float sculpt_normal_symm[3]; - - /* Used for area texture mode, local_mat gets calculated by - * calc_brush_local_mat() and used in tex_strength(). */ - float brush_local_mat[4][4]; - - float plane_offset[3]; /* used to shift the plane around when doing tiled strokes */ - int tile_pass; - - float last_center[3]; - int radial_symmetry_pass; - float symm_rot_mat[4][4]; - float symm_rot_mat_inv[4][4]; - bool original; - float anchored_location[3]; - - float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ - struct Dial *dial; - - char saved_active_brush_name[MAX_ID_NAME]; - char saved_mask_brush_tool; - int saved_smooth_size; /* smooth tool copies the size of the current tool */ - bool alt_smooth; - - float plane_trim_squared; - - bool supports_gravity; - float true_gravity_direction[3]; - float gravity_direction[3]; - - rcti previous_r; /* previous redraw rectangle */ - rcti current_r; /* current redraw rectangle */ + /* Invariants */ + float initial_radius; + float scale[3]; + int flag; + float clip_tolerance[3]; + float initial_mouse[2]; + + /* Variants */ + float radius; + float radius_squared; + float true_location[3]; + float true_last_location[3]; + float location[3]; + float last_location[3]; + bool is_last_valid; + + bool pen_flip; + bool invert; + float pressure; + float mouse[2]; + float bstrength; + float normal_weight; /* from brush (with optional override) */ + + /* The rest is temporary storage that isn't saved as a property */ + + bool first_time; /* Beginning of stroke may do some things special */ + + /* from ED_view3d_ob_project_mat_get() */ + float projection_mat[4][4]; + + /* Clean this up! */ + struct ViewContext *vc; + const struct Brush *brush; + + float special_rotation; + float grab_delta[3], grab_delta_symmetry[3]; + float old_grab_location[3], orig_grab_location[3]; + + /* screen-space rotation defined by mouse motion */ + float rake_rotation[4], rake_rotation_symmetry[4]; + bool is_rake_rotation_valid; + struct SculptRakeData rake_data; + + /* Symmetry index between 0 and 7 bit combo 0 is Brush only; + * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + int symmetry; + int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/ + float true_view_normal[3]; + float view_normal[3]; + + /* sculpt_normal gets calculated by calc_sculpt_normal(), then the + * sculpt_normal_symm gets updated quickly with the usual symmetry + * transforms */ + float sculpt_normal[3]; + float sculpt_normal_symm[3]; + + /* Used for area texture mode, local_mat gets calculated by + * calc_brush_local_mat() and used in tex_strength(). */ + float brush_local_mat[4][4]; + + float plane_offset[3]; /* used to shift the plane around when doing tiled strokes */ + int tile_pass; + + float last_center[3]; + int radial_symmetry_pass; + float symm_rot_mat[4][4]; + float symm_rot_mat_inv[4][4]; + bool original; + float anchored_location[3]; + + float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ + struct Dial *dial; + + char saved_active_brush_name[MAX_ID_NAME]; + char saved_mask_brush_tool; + int saved_smooth_size; /* smooth tool copies the size of the current tool */ + bool alt_smooth; + + float plane_trim_squared; + + bool supports_gravity; + float true_gravity_direction[3]; + float gravity_direction[3]; + + rcti previous_r; /* previous redraw rectangle */ + rcti current_r; /* current redraw rectangle */ } StrokeCache; -void sculpt_cache_calc_brushdata_symm( - StrokeCache *cache, const char symm, - const char axis, const float angle); +void sculpt_cache_calc_brushdata_symm(StrokeCache *cache, + const char symm, + const char axis, + const float angle); void sculpt_cache_free(StrokeCache *cache); SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 9c76292aaa7..0b995860feb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -70,26 +70,25 @@ #include "paint_intern.h" #include "sculpt_intern.h" - typedef struct UndoSculpt { - ListBase nodes; + ListBase nodes; - size_t undo_size; + size_t undo_size; } UndoSculpt; static UndoSculpt *sculpt_undo_get_nodes(void); static void update_cb(PBVHNode *node, void *rebuild) { - BKE_pbvh_node_mark_update(node); - if (*((bool *)rebuild)) - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_node_fully_hidden_set(node, 0); + BKE_pbvh_node_mark_update(node); + if (*((bool *)rebuild)) + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_node_fully_hidden_set(node, 0); } struct PartialUpdateData { - PBVH *pbvh; - bool rebuild; + PBVH *pbvh; + bool rebuild; }; /** @@ -97,248 +96,238 @@ struct PartialUpdateData { */ static void update_cb_partial(PBVHNode *node, void *userdata) { - struct PartialUpdateData *data = userdata; - if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) { - update_cb(node, &(data->rebuild)); - } + struct PartialUpdateData *data = userdata; + if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) { + update_cb(node, &(data->rebuild)); + } } static bool test_swap_v3_v3(float a[3], float b[3]) { - /* no need for float comparison here (memory is exactly equal or not) */ - if (memcmp(a, b, sizeof(float[3])) != 0) { - swap_v3_v3(a, b); - return true; - } - else { - return false; - } + /* no need for float comparison here (memory is exactly equal or not) */ + if (memcmp(a, b, sizeof(float[3])) != 0) { + swap_v3_v3(a, b); + return true; + } + else { + return false; + } } static bool sculpt_undo_restore_deformed( - const SculptSession *ss, - SculptUndoNode *unode, - int uindex, int oindex, - float coord[3]) + const SculptSession *ss, SculptUndoNode *unode, int uindex, int oindex, float coord[3]) { - if (test_swap_v3_v3(coord, unode->orig_co[uindex])) { - copy_v3_v3(unode->co[uindex], ss->deform_cos[oindex]); - return true; - } - else { - return false; - } + if (test_swap_v3_v3(coord, unode->orig_co[uindex])) { + copy_v3_v3(unode->co[uindex], ss->deform_cos[oindex]); + return true; + } + else { + return false; + } } static bool sculpt_undo_restore_coords(bContext *C, SculptUndoNode *unode) { - Scene *scene = CTX_data_scene(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - SculptSession *ss = ob->sculpt; - SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - MVert *mvert; - int *index; - - if (unode->maxvert) { - /* regular mesh restore */ - - if (ss->kb && !STREQ(ss->kb->name, unode->shapeName)) { - /* shape key has been changed before calling undo operator */ - - Key *key = BKE_key_from_object(ob); - KeyBlock *kb = key ? BKE_keyblock_find_name(key, unode->shapeName) : NULL; - - if (kb) { - ob->shapenr = BLI_findindex(&key->block, kb) + 1; - - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, false); - WM_event_add_notifier(C, NC_OBJECT | ND_DATA, ob); - } - else { - /* key has been removed -- skip this undo node */ - return 0; - } - } - - /* no need for float comparison here (memory is exactly equal or not) */ - index = unode->index; - mvert = ss->mvert; - - if (ss->kb) { - float (*vertCos)[3]; - vertCos = BKE_keyblock_convert_to_vertcos(ob, ss->kb); - - if (unode->orig_co) { - if (ss->modifiers_active) { - for (int i = 0; i < unode->totvert; i++) { - sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]); - } - } - else { - for (int i = 0; i < unode->totvert; i++) { - swap_v3_v3(vertCos[index[i]], unode->orig_co[i]); - } - } - } - else { - for (int i = 0; i < unode->totvert; i++) { - swap_v3_v3(vertCos[index[i]], unode->co[i]); - } - } - - /* propagate new coords to keyblock */ - sculpt_vertcos_to_key(ob, ss->kb, vertCos); - - /* pbvh uses it's own mvert array, so coords should be */ - /* propagated to pbvh here */ - BKE_pbvh_apply_vertCos(ss->pbvh, vertCos, ss->kb->totelem); - - MEM_freeN(vertCos); - } - else { - if (unode->orig_co) { - if (ss->modifiers_active) { - for (int i = 0; i < unode->totvert; i++) { - if (sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co)) { - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } - } - } - else { - for (int i = 0; i < unode->totvert; i++) { - if (test_swap_v3_v3(mvert[index[i]].co, unode->orig_co[i])) { - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - else { - for (int i = 0; i < unode->totvert; i++) { - if (test_swap_v3_v3(mvert[index[i]].co, unode->co[i])) { - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - } - else if (unode->maxgrid && subdiv_ccg != NULL) { - /* multires restore */ - CCGElem **grids, *grid; - CCGKey key; - float (*co)[3]; - int gridsize; - - grids = subdiv_ccg->grids; - gridsize = subdiv_ccg->grid_size; - BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); - - co = unode->co; - for (int j = 0; j < unode->totgrid; j++) { - grid = grids[unode->grids[j]]; - - for (int i = 0; i < gridsize * gridsize; i++, co++) { - swap_v3_v3(CCG_elem_offset_co(&key, grid, i), co[0]); - } - } - } - - return 1; + Scene *scene = CTX_data_scene(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + SculptSession *ss = ob->sculpt; + SubdivCCG *subdiv_ccg = ss->subdiv_ccg; + MVert *mvert; + int *index; + + if (unode->maxvert) { + /* regular mesh restore */ + + if (ss->kb && !STREQ(ss->kb->name, unode->shapeName)) { + /* shape key has been changed before calling undo operator */ + + Key *key = BKE_key_from_object(ob); + KeyBlock *kb = key ? BKE_keyblock_find_name(key, unode->shapeName) : NULL; + + if (kb) { + ob->shapenr = BLI_findindex(&key->block, kb) + 1; + + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, false); + WM_event_add_notifier(C, NC_OBJECT | ND_DATA, ob); + } + else { + /* key has been removed -- skip this undo node */ + return 0; + } + } + + /* no need for float comparison here (memory is exactly equal or not) */ + index = unode->index; + mvert = ss->mvert; + + if (ss->kb) { + float(*vertCos)[3]; + vertCos = BKE_keyblock_convert_to_vertcos(ob, ss->kb); + + if (unode->orig_co) { + if (ss->modifiers_active) { + for (int i = 0; i < unode->totvert; i++) { + sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]); + } + } + else { + for (int i = 0; i < unode->totvert; i++) { + swap_v3_v3(vertCos[index[i]], unode->orig_co[i]); + } + } + } + else { + for (int i = 0; i < unode->totvert; i++) { + swap_v3_v3(vertCos[index[i]], unode->co[i]); + } + } + + /* propagate new coords to keyblock */ + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + + /* pbvh uses it's own mvert array, so coords should be */ + /* propagated to pbvh here */ + BKE_pbvh_apply_vertCos(ss->pbvh, vertCos, ss->kb->totelem); + + MEM_freeN(vertCos); + } + else { + if (unode->orig_co) { + if (ss->modifiers_active) { + for (int i = 0; i < unode->totvert; i++) { + if (sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co)) { + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + } + } + } + else { + for (int i = 0; i < unode->totvert; i++) { + if (test_swap_v3_v3(mvert[index[i]].co, unode->orig_co[i])) { + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + else { + for (int i = 0; i < unode->totvert; i++) { + if (test_swap_v3_v3(mvert[index[i]].co, unode->co[i])) { + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + } + else if (unode->maxgrid && subdiv_ccg != NULL) { + /* multires restore */ + CCGElem **grids, *grid; + CCGKey key; + float(*co)[3]; + int gridsize; + + grids = subdiv_ccg->grids; + gridsize = subdiv_ccg->grid_size; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + + co = unode->co; + for (int j = 0; j < unode->totgrid; j++) { + grid = grids[unode->grids[j]]; + + for (int i = 0; i < gridsize * gridsize; i++, co++) { + swap_v3_v3(CCG_elem_offset_co(&key, grid, i), co[0]); + } + } + } + + return 1; } -static bool sculpt_undo_restore_hidden( - bContext *C, - SculptUndoNode *unode) +static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - SculptSession *ss = ob->sculpt; - SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - int i; - - if (unode->maxvert) { - MVert *mvert = ss->mvert; - - for (i = 0; i < unode->totvert; i++) { - MVert *v = &mvert[unode->index[i]]; - if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { - BLI_BITMAP_FLIP(unode->vert_hidden, i); - v->flag ^= ME_HIDE; - v->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - else if (unode->maxgrid && subdiv_ccg != NULL) { - BLI_bitmap **grid_hidden = subdiv_ccg->grid_hidden; - - for (i = 0; i < unode->totgrid; i++) { - SWAP(BLI_bitmap *, - unode->grid_hidden[i], - grid_hidden[unode->grids[i]]); - - } - } - - return 1; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + SculptSession *ss = ob->sculpt; + SubdivCCG *subdiv_ccg = ss->subdiv_ccg; + int i; + + if (unode->maxvert) { + MVert *mvert = ss->mvert; + + for (i = 0; i < unode->totvert; i++) { + MVert *v = &mvert[unode->index[i]]; + if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { + BLI_BITMAP_FLIP(unode->vert_hidden, i); + v->flag ^= ME_HIDE; + v->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + else if (unode->maxgrid && subdiv_ccg != NULL) { + BLI_bitmap **grid_hidden = subdiv_ccg->grid_hidden; + + for (i = 0; i < unode->totgrid; i++) { + SWAP(BLI_bitmap *, unode->grid_hidden[i], grid_hidden[unode->grids[i]]); + } + } + + return 1; } static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - SculptSession *ss = ob->sculpt; - SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - MVert *mvert; - float *vmask; - int *index, i, j; - - if (unode->maxvert) { - /* regular mesh restore */ - - index = unode->index; - mvert = ss->mvert; - vmask = ss->vmask; - - for (i = 0; i < unode->totvert; i++) { - if (vmask[index[i]] != unode->mask[i]) { - SWAP(float, vmask[index[i]], unode->mask[i]); - mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; - } - } - } - else if (unode->maxgrid && subdiv_ccg != NULL) { - /* multires restore */ - CCGElem **grids, *grid; - CCGKey key; - float *mask; - int gridsize; - - grids = subdiv_ccg->grids; - gridsize = subdiv_ccg->grid_size; - BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); - - mask = unode->mask; - for (j = 0; j < unode->totgrid; j++) { - grid = grids[unode->grids[j]]; - - for (i = 0; i < gridsize * gridsize; i++, mask++) - SWAP(float, *CCG_elem_offset_mask(&key, grid, i), *mask); - } - } - - return 1; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + SculptSession *ss = ob->sculpt; + SubdivCCG *subdiv_ccg = ss->subdiv_ccg; + MVert *mvert; + float *vmask; + int *index, i, j; + + if (unode->maxvert) { + /* regular mesh restore */ + + index = unode->index; + mvert = ss->mvert; + vmask = ss->vmask; + + for (i = 0; i < unode->totvert; i++) { + if (vmask[index[i]] != unode->mask[i]) { + SWAP(float, vmask[index[i]], unode->mask[i]); + mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE; + } + } + } + else if (unode->maxgrid && subdiv_ccg != NULL) { + /* multires restore */ + CCGElem **grids, *grid; + CCGKey key; + float *mask; + int gridsize; + + grids = subdiv_ccg->grids; + gridsize = subdiv_ccg->grid_size; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + + mask = unode->mask; + for (j = 0; j < unode->totgrid; j++) { + grid = grids[unode->grids[j]]; + + for (i = 0; i < gridsize * gridsize; i++, mask++) + SWAP(float, *CCG_elem_offset_mask(&key, grid, i), *mask); + } + } + + return 1; } static void sculpt_undo_bmesh_restore_generic_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) + void *__restrict userdata, const int n, const ParallelRangeTLS *__restrict UNUSED(tls)) { - PBVHNode **nodes = userdata; + PBVHNode **nodes = userdata; - BKE_pbvh_node_mark_redraw(nodes[n]); + BKE_pbvh_node_mark_redraw(nodes[n]); } static void sculpt_undo_bmesh_restore_generic(bContext *C, @@ -346,59 +335,55 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C, Object *ob, SculptSession *ss) { - if (unode->applied) { - BM_log_undo(ss->bm, ss->bm_log); - unode->applied = false; - } - else { - BM_log_redo(ss->bm, ss->bm_log); - unode->applied = true; - } - - if (unode->type == SCULPT_UNDO_MASK) { - int totnode; - PBVHNode **nodes; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - nodes, - sculpt_undo_bmesh_restore_generic_task_cb, - &settings); - - if (nodes) - MEM_freeN(nodes); - } - else { - sculpt_pbvh_clear(ob); - } + if (unode->applied) { + BM_log_undo(ss->bm, ss->bm_log); + unode->applied = false; + } + else { + BM_log_redo(ss->bm, ss->bm_log); + unode->applied = true; + } + + if (unode->type == SCULPT_UNDO_MASK) { + int totnode; + PBVHNode **nodes; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range( + 0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings); + + if (nodes) + MEM_freeN(nodes); + } + else { + sculpt_pbvh_clear(ob); + } } /* Create empty sculpt BMesh and enable logging */ -static void sculpt_undo_bmesh_enable( - Object *ob, SculptUndoNode *unode) +static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) { - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - - sculpt_pbvh_clear(ob); - - /* Create empty BMesh and enable logging */ - ss->bm = BM_mesh_create( - &bm_mesh_allocsize_default, - &((struct BMeshCreateParams){.use_toolflags = false,})); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - sculpt_dyntopo_node_layers_add(ss); - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - - /* Restore the BMLog using saved entries */ - ss->bm_log = BM_log_from_existing_entries_create(ss->bm, - unode->bm_entry); + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + /* Create empty BMesh and enable logging */ + ss->bm = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + sculpt_dyntopo_node_layers_add(ss); + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* Restore the BMLog using saved entries */ + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); } static void sculpt_undo_bmesh_restore_begin(bContext *C, @@ -406,18 +391,18 @@ static void sculpt_undo_bmesh_restore_begin(bContext *C, Object *ob, SculptSession *ss) { - if (unode->applied) { - sculpt_dynamic_topology_disable(C, unode); - unode->applied = false; - } - else { - sculpt_undo_bmesh_enable(ob, unode); - - /* Restore the mesh from the first log entry */ - BM_log_redo(ss->bm, ss->bm_log); - - unode->applied = true; - } + if (unode->applied) { + sculpt_dynamic_topology_disable(C, unode); + unode->applied = false; + } + else { + sculpt_undo_bmesh_enable(ob, unode); + + /* Restore the mesh from the first log entry */ + BM_log_redo(ss->bm, ss->bm_log); + + unode->applied = true; + } } static void sculpt_undo_bmesh_restore_end(bContext *C, @@ -425,19 +410,19 @@ static void sculpt_undo_bmesh_restore_end(bContext *C, Object *ob, SculptSession *ss) { - if (unode->applied) { - sculpt_undo_bmesh_enable(ob, unode); - - /* Restore the mesh from the last log entry */ - BM_log_undo(ss->bm, ss->bm_log); - - unode->applied = false; - } - else { - /* Disable dynamic topology sculpting */ - sculpt_dynamic_topology_disable(C, NULL); - unode->applied = true; - } + if (unode->applied) { + sculpt_undo_bmesh_enable(ob, unode); + + /* Restore the mesh from the last log entry */ + BM_log_undo(ss->bm, ss->bm_log); + + unode->applied = false; + } + else { + /* Disable dynamic topology sculpting */ + sculpt_dynamic_topology_disable(C, NULL); + unode->applied = true; + } } /* Handle all dynamic-topology updates @@ -449,565 +434,555 @@ static int sculpt_undo_bmesh_restore(bContext *C, Object *ob, SculptSession *ss) { - switch (unode->type) { - case SCULPT_UNDO_DYNTOPO_BEGIN: - sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); - return true; - - case SCULPT_UNDO_DYNTOPO_END: - sculpt_undo_bmesh_restore_end(C, unode, ob, ss); - return true; - - default: - if (ss->bm_log) { - sculpt_undo_bmesh_restore_generic(C, unode, ob, ss); - return true; - } - break; - } - - return false; + switch (unode->type) { + case SCULPT_UNDO_DYNTOPO_BEGIN: + sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); + return true; + + case SCULPT_UNDO_DYNTOPO_END: + sculpt_undo_bmesh_restore_end(C, unode, ob, ss); + return true; + + default: + if (ss->bm_log) { + sculpt_undo_bmesh_restore_generic(C, unode, ob, ss); + return true; + } + break; + } + + return false; } static void sculpt_undo_restore_list(bContext *C, ListBase *lb) { - Scene *scene = CTX_data_scene(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - SculptSession *ss = ob->sculpt; - SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - SculptUndoNode *unode; - bool update = false, rebuild = false; - bool need_mask = false; - bool partial_update = true; - - for (unode = lb->first; unode; unode = unode->next) { - if (STREQ(unode->idname, ob->id.name)) { - if (unode->type == SCULPT_UNDO_MASK) { - /* is possible that we can't do the mask undo (below) - * because of the vertex count */ - need_mask = true; - break; - } - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, need_mask); - - if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) - return; - - for (unode = lb->first; unode; unode = unode->next) { - if (!STREQ(unode->idname, ob->id.name)) - continue; - - /* check if undo data matches current data well enough to - * continue */ - if (unode->maxvert) { - if (ss->totvert != unode->maxvert) - continue; - } - else if (unode->maxgrid && subdiv_ccg != NULL) { - if ((subdiv_ccg->num_grids != unode->maxgrid) || - (subdiv_ccg->grid_size != unode->gridsize)) - { - continue; - } - - /* multi-res can't do partial updates since it doesn't flag edited vertices */ - partial_update = false; - } - - switch (unode->type) { - case SCULPT_UNDO_COORDS: - if (sculpt_undo_restore_coords(C, unode)) - update = true; - break; - case SCULPT_UNDO_HIDDEN: - if (sculpt_undo_restore_hidden(C, unode)) - rebuild = true; - break; - case SCULPT_UNDO_MASK: - if (sculpt_undo_restore_mask(C, unode)) - update = true; - break; - - case SCULPT_UNDO_DYNTOPO_BEGIN: - case SCULPT_UNDO_DYNTOPO_END: - case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - BLI_assert(!"Dynamic topology should've already been handled"); - break; - } - } - - if (update || rebuild) { - bool tag_update = false; - /* we update all nodes still, should be more clever, but also - * needs to work correct when exiting/entering sculpt mode and - * the nodes get recreated, though in that case it could do all */ - if (partial_update) { - struct PartialUpdateData data = { - .rebuild = rebuild, - .pbvh = ss->pbvh, - }; - BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); - } - else { - BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); - } - BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw | PBVH_UpdateNormals, NULL); - - if (BKE_sculpt_multires_active(scene, ob)) { - if (rebuild) - multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED); - else - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); - } - - tag_update |= ((Mesh *)ob->data)->id.us > 1; - - if (ss->kb || ss->modifiers_active) { - Mesh *mesh = ob->data; - BKE_mesh_calc_normals(mesh); - - BKE_sculptsession_free_deformMats(ss); - tag_update |= true; - } - - if (tag_update) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - else { - sculpt_update_object_bounding_box(ob); - } - } + Scene *scene = CTX_data_scene(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + SculptSession *ss = ob->sculpt; + SubdivCCG *subdiv_ccg = ss->subdiv_ccg; + SculptUndoNode *unode; + bool update = false, rebuild = false; + bool need_mask = false; + bool partial_update = true; + + for (unode = lb->first; unode; unode = unode->next) { + if (STREQ(unode->idname, ob->id.name)) { + if (unode->type == SCULPT_UNDO_MASK) { + /* is possible that we can't do the mask undo (below) + * because of the vertex count */ + need_mask = true; + break; + } + } + } + + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, need_mask); + + if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) + return; + + for (unode = lb->first; unode; unode = unode->next) { + if (!STREQ(unode->idname, ob->id.name)) + continue; + + /* check if undo data matches current data well enough to + * continue */ + if (unode->maxvert) { + if (ss->totvert != unode->maxvert) + continue; + } + else if (unode->maxgrid && subdiv_ccg != NULL) { + if ((subdiv_ccg->num_grids != unode->maxgrid) || + (subdiv_ccg->grid_size != unode->gridsize)) { + continue; + } + + /* multi-res can't do partial updates since it doesn't flag edited vertices */ + partial_update = false; + } + + switch (unode->type) { + case SCULPT_UNDO_COORDS: + if (sculpt_undo_restore_coords(C, unode)) + update = true; + break; + case SCULPT_UNDO_HIDDEN: + if (sculpt_undo_restore_hidden(C, unode)) + rebuild = true; + break; + case SCULPT_UNDO_MASK: + if (sculpt_undo_restore_mask(C, unode)) + update = true; + break; + + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; + } + } + + if (update || rebuild) { + bool tag_update = false; + /* we update all nodes still, should be more clever, but also + * needs to work correct when exiting/entering sculpt mode and + * the nodes get recreated, though in that case it could do all */ + if (partial_update) { + struct PartialUpdateData data = { + .rebuild = rebuild, + .pbvh = ss->pbvh, + }; + BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); + } + else { + BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); + } + BKE_pbvh_update(ss->pbvh, + PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw | PBVH_UpdateNormals, + NULL); + + if (BKE_sculpt_multires_active(scene, ob)) { + if (rebuild) + multires_mark_as_modified(ob, MULTIRES_HIDDEN_MODIFIED); + else + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + } + + tag_update |= ((Mesh *)ob->data)->id.us > 1; + + if (ss->kb || ss->modifiers_active) { + Mesh *mesh = ob->data; + BKE_mesh_calc_normals(mesh); + + BKE_sculptsession_free_deformMats(ss); + tag_update |= true; + } + + if (tag_update) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + else { + sculpt_update_object_bounding_box(ob); + } + } } static void sculpt_undo_free_list(ListBase *lb) { - SculptUndoNode *unode = lb->first; - while (unode != NULL) { - SculptUndoNode *unode_next = unode->next; - if (unode->co) - MEM_freeN(unode->co); - if (unode->no) - MEM_freeN(unode->no); - if (unode->index) - MEM_freeN(unode->index); - if (unode->grids) - MEM_freeN(unode->grids); - if (unode->orig_co) - MEM_freeN(unode->orig_co); - if (unode->vert_hidden) - MEM_freeN(unode->vert_hidden); - if (unode->grid_hidden) { - for (int i = 0; i < unode->totgrid; i++) { - if (unode->grid_hidden[i]) - MEM_freeN(unode->grid_hidden[i]); - } - MEM_freeN(unode->grid_hidden); - } - if (unode->mask) - MEM_freeN(unode->mask); - - if (unode->bm_entry) { - BM_log_entry_drop(unode->bm_entry); - } - - if (unode->bm_enter_totvert) - CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); - if (unode->bm_enter_totedge) - CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge); - if (unode->bm_enter_totloop) - CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop); - if (unode->bm_enter_totpoly) - CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly); - - MEM_freeN(unode); - - unode = unode_next; - } + SculptUndoNode *unode = lb->first; + while (unode != NULL) { + SculptUndoNode *unode_next = unode->next; + if (unode->co) + MEM_freeN(unode->co); + if (unode->no) + MEM_freeN(unode->no); + if (unode->index) + MEM_freeN(unode->index); + if (unode->grids) + MEM_freeN(unode->grids); + if (unode->orig_co) + MEM_freeN(unode->orig_co); + if (unode->vert_hidden) + MEM_freeN(unode->vert_hidden); + if (unode->grid_hidden) { + for (int i = 0; i < unode->totgrid; i++) { + if (unode->grid_hidden[i]) + MEM_freeN(unode->grid_hidden[i]); + } + MEM_freeN(unode->grid_hidden); + } + if (unode->mask) + MEM_freeN(unode->mask); + + if (unode->bm_entry) { + BM_log_entry_drop(unode->bm_entry); + } + + if (unode->bm_enter_totvert) + CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); + if (unode->bm_enter_totedge) + CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge); + if (unode->bm_enter_totloop) + CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop); + if (unode->bm_enter_totpoly) + CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly); + + MEM_freeN(unode); + + unode = unode_next; + } } /* Most likely we don't need this. */ #if 0 static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - SculptUndoNode *unode; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + SculptUndoNode *unode; - unode = lb->first; + unode = lb->first; - if (unode && !STREQ(unode->idname, ob->id.name)) { - if (unode->bm_entry) - BM_log_cleanup_entry(unode->bm_entry); + if (unode && !STREQ(unode->idname, ob->id.name)) { + if (unode->bm_entry) + BM_log_cleanup_entry(unode->bm_entry); - return true; - } + return true; + } - return false; + return false; } #endif SculptUndoNode *sculpt_undo_get_node(PBVHNode *node) { - UndoSculpt *usculpt = sculpt_undo_get_nodes(); + UndoSculpt *usculpt = sculpt_undo_get_nodes(); - if (usculpt == NULL) { - return NULL; - } + if (usculpt == NULL) { + return NULL; + } - return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); + return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); } -static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, - SculptUndoNode *unode) +static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, SculptUndoNode *unode) { - PBVHNode *node = unode->node; - BLI_bitmap **grid_hidden; - int i, *grid_indices, totgrid; + PBVHNode *node = unode->node; + BLI_bitmap **grid_hidden; + int i, *grid_indices, totgrid; - grid_hidden = BKE_pbvh_grid_hidden(pbvh); + grid_hidden = BKE_pbvh_grid_hidden(pbvh); - BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, - NULL, NULL, NULL); + BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, NULL); - unode->grid_hidden = MEM_mapallocN(sizeof(*unode->grid_hidden) * totgrid, - "unode->grid_hidden"); + unode->grid_hidden = MEM_mapallocN(sizeof(*unode->grid_hidden) * totgrid, "unode->grid_hidden"); - for (i = 0; i < totgrid; i++) { - if (grid_hidden[grid_indices[i]]) - unode->grid_hidden[i] = MEM_dupallocN(grid_hidden[grid_indices[i]]); - else - unode->grid_hidden[i] = NULL; - } + for (i = 0; i < totgrid; i++) { + if (grid_hidden[grid_indices[i]]) + unode->grid_hidden[i] = MEM_dupallocN(grid_hidden[grid_indices[i]]); + else + unode->grid_hidden[i] = NULL; + } } -static SculptUndoNode *sculpt_undo_alloc_node( - Object *ob, PBVHNode *node, - SculptUndoType type) +static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, SculptUndoType type) { - UndoSculpt *usculpt = sculpt_undo_get_nodes(); - SculptUndoNode *unode; - SculptSession *ss = ob->sculpt; - int totvert, allvert, totgrid, maxgrid, gridsize, *grids; - - unode = MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); - BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); - unode->type = type; - unode->node = node; - - if (node) { - BKE_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); - BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL); - - unode->totvert = totvert; - } - else - maxgrid = 0; - - /* we will use this while sculpting, is mapalloc slow to access then? */ - - /* general TODO, fix count_alloc */ - switch (type) { - case SCULPT_UNDO_COORDS: - unode->co = MEM_mapallocN(sizeof(float[3]) * allvert, "SculptUndoNode.co"); - unode->no = MEM_mapallocN(sizeof(short[3]) * allvert, "SculptUndoNode.no"); - - usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert; - break; - case SCULPT_UNDO_HIDDEN: - if (maxgrid) - sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode); - else - unode->vert_hidden = BLI_BITMAP_NEW(allvert, "SculptUndoNode.vert_hidden"); - - break; - case SCULPT_UNDO_MASK: - unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask"); - - usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; - - break; - case SCULPT_UNDO_DYNTOPO_BEGIN: - case SCULPT_UNDO_DYNTOPO_END: - case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - BLI_assert(!"Dynamic topology should've already been handled"); - break; - } - - BLI_addtail(&usculpt->nodes, unode); - - if (maxgrid) { - /* multires */ - unode->maxgrid = maxgrid; - unode->totgrid = totgrid; - unode->gridsize = gridsize; - unode->grids = MEM_mapallocN(sizeof(int) * totgrid, "SculptUndoNode.grids"); - } - else { - /* regular mesh */ - unode->maxvert = ss->totvert; - unode->index = MEM_mapallocN(sizeof(int) * allvert, "SculptUndoNode.index"); - } - - if (ss->modifiers_active) - unode->orig_co = MEM_callocN(allvert * sizeof(*unode->orig_co), "undoSculpt orig_cos"); - - return unode; + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + SculptUndoNode *unode; + SculptSession *ss = ob->sculpt; + int totvert, allvert, totgrid, maxgrid, gridsize, *grids; + + unode = MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + unode->type = type; + unode->node = node; + + if (node) { + BKE_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); + BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, &maxgrid, &gridsize, NULL); + + unode->totvert = totvert; + } + else + maxgrid = 0; + + /* we will use this while sculpting, is mapalloc slow to access then? */ + + /* general TODO, fix count_alloc */ + switch (type) { + case SCULPT_UNDO_COORDS: + unode->co = MEM_mapallocN(sizeof(float[3]) * allvert, "SculptUndoNode.co"); + unode->no = MEM_mapallocN(sizeof(short[3]) * allvert, "SculptUndoNode.no"); + + usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert; + break; + case SCULPT_UNDO_HIDDEN: + if (maxgrid) + sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode); + else + unode->vert_hidden = BLI_BITMAP_NEW(allvert, "SculptUndoNode.vert_hidden"); + + break; + case SCULPT_UNDO_MASK: + unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask"); + + usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; + + break; + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; + } + + BLI_addtail(&usculpt->nodes, unode); + + if (maxgrid) { + /* multires */ + unode->maxgrid = maxgrid; + unode->totgrid = totgrid; + unode->gridsize = gridsize; + unode->grids = MEM_mapallocN(sizeof(int) * totgrid, "SculptUndoNode.grids"); + } + else { + /* regular mesh */ + unode->maxvert = ss->totvert; + unode->index = MEM_mapallocN(sizeof(int) * allvert, "SculptUndoNode.index"); + } + + if (ss->modifiers_active) + unode->orig_co = MEM_callocN(allvert * sizeof(*unode->orig_co), "undoSculpt orig_cos"); + + return unode; } static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) { - SculptSession *ss = ob->sculpt; - PBVHVertexIter vd; - - BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) - { - copy_v3_v3(unode->co[vd.i], vd.co); - if (vd.no) copy_v3_v3_short(unode->no[vd.i], vd.no); - else normal_float_to_short_v3(unode->no[vd.i], vd.fno); - - if (ss->modifiers_active) - copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]); - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) + { + copy_v3_v3(unode->co[vd.i], vd.co); + if (vd.no) + copy_v3_v3_short(unode->no[vd.i], vd.no); + else + normal_float_to_short_v3(unode->no[vd.i], vd.fno); + + if (ss->modifiers_active) + copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]); + } + BKE_pbvh_vertex_iter_end; } static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) { - PBVH *pbvh = ob->sculpt->pbvh; - PBVHNode *node = unode->node; - - if (unode->grids) { - /* already stored during allocation */ - } - else { - MVert *mvert; - const int *vert_indices; - int allvert; - int i; - - BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); - BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); - for (i = 0; i < allvert; i++) { - BLI_BITMAP_SET(unode->vert_hidden, i, - mvert[vert_indices[i]].flag & ME_HIDE); - } - } + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode *node = unode->node; + + if (unode->grids) { + /* already stored during allocation */ + } + else { + MVert *mvert; + const int *vert_indices; + int allvert; + int i; + + BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); + BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); + for (i = 0; i < allvert; i++) { + BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE); + } + } } static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) { - SculptSession *ss = ob->sculpt; - PBVHVertexIter vd; - - BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) - { - unode->mask[vd.i] = *vd.mask; - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) + { + unode->mask[vd.i] = *vd.mask; + } + BKE_pbvh_vertex_iter_end; } -static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, - PBVHNode *node, - SculptUndoType type) +static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) { - UndoSculpt *usculpt = sculpt_undo_get_nodes(); - SculptSession *ss = ob->sculpt; - PBVHVertexIter vd; - - SculptUndoNode *unode = usculpt->nodes.first; - - if (unode == NULL) { - unode = MEM_callocN(sizeof(*unode), __func__); - - BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); - unode->type = type; - unode->applied = true; - - if (type == SCULPT_UNDO_DYNTOPO_END) { - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_before_all_removed(ss->bm, ss->bm_log); - } - else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { - Mesh *me = ob->data; - - /* Store a copy of the mesh's current vertices, loops, and - * polys. A full copy like this is needed because entering - * dynamic-topology immediately does topological edits - * (converting polys to triangles) that the BMLog can't - * fully restore from */ - CustomData_copy(&me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH.vmask, - CD_DUPLICATE, me->totvert); - CustomData_copy(&me->edata, &unode->bm_enter_edata, CD_MASK_MESH.emask, - CD_DUPLICATE, me->totedge); - CustomData_copy(&me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH.lmask, - CD_DUPLICATE, me->totloop); - CustomData_copy(&me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH.pmask, - CD_DUPLICATE, me->totpoly); - unode->bm_enter_totvert = me->totvert; - unode->bm_enter_totedge = me->totedge; - unode->bm_enter_totloop = me->totloop; - unode->bm_enter_totpoly = me->totpoly; - - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_all_added(ss->bm, ss->bm_log); - } - else { - unode->bm_entry = BM_log_entry_add(ss->bm_log); - } - - BLI_addtail(&usculpt->nodes, unode); - } - - if (node) { - switch (type) { - case SCULPT_UNDO_COORDS: - case SCULPT_UNDO_MASK: - /* Before any vertex values get modified, ensure their - * original positions are logged */ - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { - BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); - } - BKE_pbvh_vertex_iter_end; - break; - - case SCULPT_UNDO_HIDDEN: - { - GSetIterator gs_iter; - GSet *faces = BKE_pbvh_bmesh_node_faces(node); - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { - BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); - } - BKE_pbvh_vertex_iter_end; - - GSET_ITER (gs_iter, faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); - BM_log_face_modified(ss->bm_log, f); - } - break; - } - - case SCULPT_UNDO_DYNTOPO_BEGIN: - case SCULPT_UNDO_DYNTOPO_END: - case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - break; - } - } - - return unode; + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + SculptSession *ss = ob->sculpt; + PBVHVertexIter vd; + + SculptUndoNode *unode = usculpt->nodes.first; + + if (unode == NULL) { + unode = MEM_callocN(sizeof(*unode), __func__); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + unode->type = type; + unode->applied = true; + + if (type == SCULPT_UNDO_DYNTOPO_END) { + unode->bm_entry = BM_log_entry_add(ss->bm_log); + BM_log_before_all_removed(ss->bm, ss->bm_log); + } + else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { + Mesh *me = ob->data; + + /* Store a copy of the mesh's current vertices, loops, and + * polys. A full copy like this is needed because entering + * dynamic-topology immediately does topological edits + * (converting polys to triangles) that the BMLog can't + * fully restore from */ + CustomData_copy( + &me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert); + CustomData_copy( + &me->edata, &unode->bm_enter_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge); + CustomData_copy( + &me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop); + CustomData_copy( + &me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly); + unode->bm_enter_totvert = me->totvert; + unode->bm_enter_totedge = me->totedge; + unode->bm_enter_totloop = me->totloop; + unode->bm_enter_totpoly = me->totpoly; + + unode->bm_entry = BM_log_entry_add(ss->bm_log); + BM_log_all_added(ss->bm, ss->bm_log); + } + else { + unode->bm_entry = BM_log_entry_add(ss->bm_log); + } + + BLI_addtail(&usculpt->nodes, unode); + } + + if (node) { + switch (type) { + case SCULPT_UNDO_COORDS: + case SCULPT_UNDO_MASK: + /* Before any vertex values get modified, ensure their + * original positions are logged */ + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); + } + BKE_pbvh_vertex_iter_end; + break; + + case SCULPT_UNDO_HIDDEN: { + GSetIterator gs_iter; + GSet *faces = BKE_pbvh_bmesh_node_faces(node); + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); + } + BKE_pbvh_vertex_iter_end; + + GSET_ITER (gs_iter, faces) { + BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + BM_log_face_modified(ss->bm_log, f); + } + break; + } + + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + break; + } + } + + return unode; } -SculptUndoNode *sculpt_undo_push_node( - Object *ob, PBVHNode *node, - SculptUndoType type) +SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) { - SculptSession *ss = ob->sculpt; - SculptUndoNode *unode; - - /* list is manipulated by multiple threads, so we lock */ - BLI_thread_lock(LOCK_CUSTOM1); - - if (ss->bm || - ELEM(type, - SCULPT_UNDO_DYNTOPO_BEGIN, - SCULPT_UNDO_DYNTOPO_END)) - { - /* Dynamic topology stores only one undo node per stroke, - * regardless of the number of PBVH nodes modified */ - unode = sculpt_undo_bmesh_push(ob, node, type); - BLI_thread_unlock(LOCK_CUSTOM1); - return unode; - } - else if ((unode = sculpt_undo_get_node(node))) { - BLI_thread_unlock(LOCK_CUSTOM1); - return unode; - } - - unode = sculpt_undo_alloc_node(ob, node, type); - - /* NOTE: If this ever becomes a bottleneck, make a lock inside of the node. - * so we release global lock sooner, but keep data locked for until it is - * fully initialized. - */ - - if (unode->grids) { - int totgrid, *grids; - BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - NULL, NULL, NULL); - memcpy(unode->grids, grids, sizeof(int) * totgrid); - } - else { - const int *vert_indices; - int allvert; - BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); - BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); - memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); - } - - switch (type) { - case SCULPT_UNDO_COORDS: - sculpt_undo_store_coords(ob, unode); - break; - case SCULPT_UNDO_HIDDEN: - sculpt_undo_store_hidden(ob, unode); - break; - case SCULPT_UNDO_MASK: - sculpt_undo_store_mask(ob, unode); - break; - case SCULPT_UNDO_DYNTOPO_BEGIN: - case SCULPT_UNDO_DYNTOPO_END: - case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - BLI_assert(!"Dynamic topology should've already been handled"); - break; - } - - /* store active shape key */ - if (ss->kb) BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); - else unode->shapeName[0] = '\0'; - - BLI_thread_unlock(LOCK_CUSTOM1); - - return unode; + SculptSession *ss = ob->sculpt; + SculptUndoNode *unode; + + /* list is manipulated by multiple threads, so we lock */ + BLI_thread_lock(LOCK_CUSTOM1); + + if (ss->bm || ELEM(type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { + /* Dynamic topology stores only one undo node per stroke, + * regardless of the number of PBVH nodes modified */ + unode = sculpt_undo_bmesh_push(ob, node, type); + BLI_thread_unlock(LOCK_CUSTOM1); + return unode; + } + else if ((unode = sculpt_undo_get_node(node))) { + BLI_thread_unlock(LOCK_CUSTOM1); + return unode; + } + + unode = sculpt_undo_alloc_node(ob, node, type); + + /* NOTE: If this ever becomes a bottleneck, make a lock inside of the node. + * so we release global lock sooner, but keep data locked for until it is + * fully initialized. + */ + + if (unode->grids) { + int totgrid, *grids; + BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, NULL, NULL, NULL); + memcpy(unode->grids, grids, sizeof(int) * totgrid); + } + else { + const int *vert_indices; + int allvert; + BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); + BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); + memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); + } + + switch (type) { + case SCULPT_UNDO_COORDS: + sculpt_undo_store_coords(ob, unode); + break; + case SCULPT_UNDO_HIDDEN: + sculpt_undo_store_hidden(ob, unode); + break; + case SCULPT_UNDO_MASK: + sculpt_undo_store_mask(ob, unode); + break; + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; + } + + /* store active shape key */ + if (ss->kb) + BLI_strncpy(unode->shapeName, ss->kb->name, sizeof(ss->kb->name)); + else + unode->shapeName[0] = '\0'; + + BLI_thread_unlock(LOCK_CUSTOM1); + + return unode; } void sculpt_undo_push_begin(const char *name) { - UndoStack *ustack = ED_undo_stack_get(); - bContext *C = NULL; /* special case, we never read from this. */ - BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); + UndoStack *ustack = ED_undo_stack_get(); + bContext *C = NULL; /* special case, we never read from this. */ + BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); } void sculpt_undo_push_end(void) { - UndoSculpt *usculpt = sculpt_undo_get_nodes(); - SculptUndoNode *unode; - - /* we don't need normals in the undo stack */ - for (unode = usculpt->nodes.first; unode; unode = unode->next) { - if (unode->no) { - MEM_freeN(unode->no); - unode->no = NULL; - } - - if (unode->node) - BKE_pbvh_node_layer_disp_free(unode->node); - } - - /* We could remove this and enforce all callers run in an operator using 'OPTYPE_UNDO'. */ - wmWindowManager *wm = G_MAIN->wm.first; - if (wm->op_undo_depth == 0) { - UndoStack *ustack = ED_undo_stack_get(); - BKE_undosys_step_push(ustack, NULL, NULL); - } + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + SculptUndoNode *unode; + + /* we don't need normals in the undo stack */ + for (unode = usculpt->nodes.first; unode; unode = unode->next) { + if (unode->no) { + MEM_freeN(unode->no); + unode->no = NULL; + } + + if (unode->node) + BKE_pbvh_node_layer_disp_free(unode->node); + } + + /* We could remove this and enforce all callers run in an operator using 'OPTYPE_UNDO'. */ + wmWindowManager *wm = G_MAIN->wm.first; + if (wm->op_undo_depth == 0) { + UndoStack *ustack = ED_undo_stack_get(); + BKE_undosys_step_push(ustack, NULL, NULL); + } } /* -------------------------------------------------------------------- */ @@ -1015,149 +990,154 @@ void sculpt_undo_push_end(void) * \{ */ typedef struct SculptUndoStep { - UndoStep step; - /* note: will split out into list for multi-object-sculpt-mode. */ - UndoSculpt data; + UndoStep step; + /* note: will split out into list for multi-object-sculpt-mode. */ + UndoSculpt data; } SculptUndoStep; static bool sculpt_undosys_poll(bContext *C) { - Object *obact = CTX_data_active_object(C); - if (obact && obact->type == OB_MESH) { - if (obact && (obact->mode & OB_MODE_SCULPT)) { - return true; - } - } - return false; + Object *obact = CTX_data_active_object(C); + if (obact && obact->type == OB_MESH) { + if (obact && (obact->mode & OB_MODE_SCULPT)) { + return true; + } + } + return false; } static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { - SculptUndoStep *us = (SculptUndoStep *)us_p; - /* dummy, memory is cleared anyway. */ - BLI_listbase_clear(&us->data.nodes); + SculptUndoStep *us = (SculptUndoStep *)us_p; + /* dummy, memory is cleared anyway. */ + BLI_listbase_clear(&us->data.nodes); } -static bool sculpt_undosys_step_encode(struct bContext *UNUSED(C), struct Main *UNUSED(bmain), UndoStep *us_p) +static bool sculpt_undosys_step_encode(struct bContext *UNUSED(C), + struct Main *UNUSED(bmain), + UndoStep *us_p) { - /* dummy, encoding is done along the way by adding tiles - * to the current 'SculptUndoStep' added by encode_init. */ - SculptUndoStep *us = (SculptUndoStep *)us_p; - us->step.data_size = us->data.undo_size; - - SculptUndoNode *unode = us->data.nodes.last; - if (unode && unode->type == SCULPT_UNDO_DYNTOPO_END) { - us->step.use_memfile_step = true; - } - us->step.is_applied = true; - return true; + /* dummy, encoding is done along the way by adding tiles + * to the current 'SculptUndoStep' added by encode_init. */ + SculptUndoStep *us = (SculptUndoStep *)us_p; + us->step.data_size = us->data.undo_size; + + SculptUndoNode *unode = us->data.nodes.last; + if (unode && unode->type == SCULPT_UNDO_DYNTOPO_END) { + us->step.use_memfile_step = true; + } + us->step.is_applied = true; + return true; } static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, SculptUndoStep *us) { - BLI_assert(us->step.is_applied == true); - sculpt_undo_restore_list(C, &us->data.nodes); - us->step.is_applied = false; + BLI_assert(us->step.is_applied == true); + sculpt_undo_restore_list(C, &us->data.nodes); + us->step.is_applied = false; } static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, SculptUndoStep *us) { - BLI_assert(us->step.is_applied == false); - sculpt_undo_restore_list(C, &us->data.nodes); - us->step.is_applied = true; + BLI_assert(us->step.is_applied == false); + sculpt_undo_restore_list(C, &us->data.nodes); + us->step.is_applied = true; } static void sculpt_undosys_step_decode_undo(struct bContext *C, SculptUndoStep *us) { - SculptUndoStep *us_iter = us; - while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { - if (us_iter->step.next->is_applied == false) { - break; - } - us_iter = (SculptUndoStep *)us_iter->step.next; - } - while (us_iter != us) { - sculpt_undosys_step_decode_undo_impl(C, us_iter); - us_iter = (SculptUndoStep *)us_iter->step.prev; - } + SculptUndoStep *us_iter = us; + while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { + if (us_iter->step.next->is_applied == false) { + break; + } + us_iter = (SculptUndoStep *)us_iter->step.next; + } + while (us_iter != us) { + sculpt_undosys_step_decode_undo_impl(C, us_iter); + us_iter = (SculptUndoStep *)us_iter->step.prev; + } } static void sculpt_undosys_step_decode_redo(struct bContext *C, SculptUndoStep *us) { - SculptUndoStep *us_iter = us; - while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { - if (us_iter->step.prev->is_applied == true) { - break; - } - us_iter = (SculptUndoStep *)us_iter->step.prev; - } - while (us_iter && (us_iter->step.is_applied == false)) { - sculpt_undosys_step_decode_redo_impl(C, us_iter); - if (us_iter == us) { - break; - } - us_iter = (SculptUndoStep *)us_iter->step.next; - } + SculptUndoStep *us_iter = us; + while (us_iter->step.prev && (us_iter->step.prev->type == us_iter->step.type)) { + if (us_iter->step.prev->is_applied == true) { + break; + } + us_iter = (SculptUndoStep *)us_iter->step.prev; + } + while (us_iter && (us_iter->step.is_applied == false)) { + sculpt_undosys_step_decode_redo_impl(C, us_iter); + if (us_iter == us) { + break; + } + us_iter = (SculptUndoStep *)us_iter->step.next; + } } -static void sculpt_undosys_step_decode(struct bContext *C, struct Main *bmain, UndoStep *us_p, int dir) +static void sculpt_undosys_step_decode(struct bContext *C, + struct Main *bmain, + UndoStep *us_p, + int dir) { - /* Ensure sculpt mode. */ - { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - /* Sculpt needs evaluated state. */ - BKE_scene_view_layer_graph_evaluated_ensure(bmain, scene, view_layer); - Object *ob = OBACT(view_layer); - if (ob && (ob->type == OB_MESH)) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - if (ob->mode & OB_MODE_SCULPT) { - /* pass */ - } - else { - ED_object_mode_generic_exit(bmain, depsgraph, scene, ob); - Mesh *me = ob->data; - /* Don't add sculpt topology undo steps when reading back undo state. - * The undo steps must enter/exit for us. */ - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, NULL); - } - BLI_assert(sculpt_undosys_poll(C)); - } - else { - BLI_assert(0); - return; - } - } - - SculptUndoStep *us = (SculptUndoStep *)us_p; - if (dir < 0) { - sculpt_undosys_step_decode_undo(C, us); - } - else { - sculpt_undosys_step_decode_redo(C, us); - } + /* Ensure sculpt mode. */ + { + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + /* Sculpt needs evaluated state. */ + BKE_scene_view_layer_graph_evaluated_ensure(bmain, scene, view_layer); + Object *ob = OBACT(view_layer); + if (ob && (ob->type == OB_MESH)) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); + if (ob->mode & OB_MODE_SCULPT) { + /* pass */ + } + else { + ED_object_mode_generic_exit(bmain, depsgraph, scene, ob); + Mesh *me = ob->data; + /* Don't add sculpt topology undo steps when reading back undo state. + * The undo steps must enter/exit for us. */ + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, NULL); + } + BLI_assert(sculpt_undosys_poll(C)); + } + else { + BLI_assert(0); + return; + } + } + + SculptUndoStep *us = (SculptUndoStep *)us_p; + if (dir < 0) { + sculpt_undosys_step_decode_undo(C, us); + } + else { + sculpt_undosys_step_decode_redo(C, us); + } } static void sculpt_undosys_step_free(UndoStep *us_p) { - SculptUndoStep *us = (SculptUndoStep *)us_p; - sculpt_undo_free_list(&us->data.nodes); + SculptUndoStep *us = (SculptUndoStep *)us_p; + sculpt_undo_free_list(&us->data.nodes); } /* Export for ED_undo_sys. */ void ED_sculpt_undosys_type(UndoType *ut) { - ut->name = "Sculpt"; - ut->poll = sculpt_undosys_poll; - ut->step_encode_init = sculpt_undosys_step_encode_init; - ut->step_encode = sculpt_undosys_step_encode; - ut->step_decode = sculpt_undosys_step_decode; - ut->step_free = sculpt_undosys_step_free; + ut->name = "Sculpt"; + ut->poll = sculpt_undosys_poll; + ut->step_encode_init = sculpt_undosys_step_encode_init; + ut->step_encode = sculpt_undosys_step_encode; + ut->step_decode = sculpt_undosys_step_decode; + ut->step_free = sculpt_undosys_step_free; - ut->use_context = true; + ut->use_context = true; - ut->step_size = sizeof(SculptUndoStep); + ut->step_size = sizeof(SculptUndoStep); } /** \} */ @@ -1168,15 +1148,15 @@ void ED_sculpt_undosys_type(UndoType *ut) static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p) { - SculptUndoStep *us = (SculptUndoStep *)us_p; - return &us->data; + SculptUndoStep *us = (SculptUndoStep *)us_p; + return &us->data; } static UndoSculpt *sculpt_undo_get_nodes(void) { - UndoStack *ustack = ED_undo_stack_get(); - UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); - return sculpt_undosys_step_get_nodes(us); + UndoStack *ustack = ED_undo_stack_get(); + UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); + return sculpt_undosys_step_get_nodes(us); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index dde09bb0f45..39ada703b9b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -22,7 +22,6 @@ * \ingroup edsculpt */ - #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" @@ -64,126 +63,123 @@ #include "UI_view2d.h" -#define MARK_BOUNDARY 1 +#define MARK_BOUNDARY 1 typedef struct UvAdjacencyElement { - /* pointer to original uvelement */ - UvElement *element; - /* uv pointer for convenience. Caution, this points to the original UVs! */ - float *uv; - /* general use flag (Used to check if Element is boundary here) */ - char flag; + /* pointer to original uvelement */ + UvElement *element; + /* uv pointer for convenience. Caution, this points to the original UVs! */ + float *uv; + /* general use flag (Used to check if Element is boundary here) */ + char flag; } UvAdjacencyElement; typedef struct UvEdge { - unsigned int uv1; - unsigned int uv2; - /* general use flag - * (Used to check if edge is boundary here, and propagates to adjacency elements) */ - char flag; + unsigned int uv1; + unsigned int uv2; + /* general use flag + * (Used to check if edge is boundary here, and propagates to adjacency elements) */ + char flag; } UvEdge; typedef struct UVInitialStrokeElement { - /* index to unique uv */ - int uv; + /* index to unique uv */ + int uv; - /* strength of brush on initial position */ - float strength; + /* strength of brush on initial position */ + float strength; - /* initial uv position */ - float initial_uv[2]; + /* initial uv position */ + float initial_uv[2]; } UVInitialStrokeElement; typedef struct UVInitialStroke { - /* Initial Selection,for grab brushes for instance */ - UVInitialStrokeElement *initialSelection; + /* Initial Selection,for grab brushes for instance */ + UVInitialStrokeElement *initialSelection; - /* total initially selected UVs*/ - int totalInitialSelected; + /* total initially selected UVs*/ + int totalInitialSelected; - /* initial mouse coordinates */ - float init_coord[2]; + /* initial mouse coordinates */ + float init_coord[2]; } UVInitialStroke; - /* custom data for uv smoothing brush */ typedef struct UvSculptData { - /* Contains the first of each set of coincident uvs. - * These will be used to perform smoothing on and propagate the changes - * to their coincident uvs */ - UvAdjacencyElement *uv; + /* Contains the first of each set of coincident uvs. + * These will be used to perform smoothing on and propagate the changes + * to their coincident uvs */ + UvAdjacencyElement *uv; - /* ...Is what it says */ - int totalUniqueUvs; + /* ...Is what it says */ + int totalUniqueUvs; - /* Edges used for adjacency info, used with laplacian smoothing */ - UvEdge *uvedges; + /* Edges used for adjacency info, used with laplacian smoothing */ + UvEdge *uvedges; - /* need I say more? */ - int totalUvEdges; + /* need I say more? */ + int totalUvEdges; - /* data for initial stroke, used by tools like grab */ - UVInitialStroke *initial_stroke; + /* data for initial stroke, used by tools like grab */ + UVInitialStroke *initial_stroke; - /* timer to be used for airbrush-type brush */ - wmTimer *timer; + /* timer to be used for airbrush-type brush */ + wmTimer *timer; - /* to determine quickly adjacent uvs */ - UvElementMap *elementMap; + /* to determine quickly adjacent uvs */ + UvElementMap *elementMap; - /* uvsmooth Paint for fast reference */ - Paint *uvsculpt; + /* uvsmooth Paint for fast reference */ + Paint *uvsculpt; - /* tool to use. duplicating here to change if modifier keys are pressed */ - char tool; + /* tool to use. duplicating here to change if modifier keys are pressed */ + char tool; - /* store invert flag here */ - char invert; + /* store invert flag here */ + char invert; } UvSculptData; - static Brush *uv_sculpt_brush(bContext *C) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; - if (!settings->uvsculpt) - return NULL; - return BKE_paint_brush(&settings->uvsculpt->paint); + if (!settings->uvsculpt) + return NULL; + return BKE_paint_brush(&settings->uvsculpt->paint); } - static bool uv_sculpt_brush_poll_do(bContext *C, const bool check_region) { - BMEditMesh *em; - int ret; - Object *obedit = CTX_data_edit_object(C); - SpaceImage *sima = CTX_wm_space_image(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = scene->toolsettings; - - if (!uv_sculpt_brush(C) || !obedit || obedit->type != OB_MESH || - !sima || ED_space_image_show_render(sima) || (sima->mode == SI_MODE_PAINT)) - { - return 0; - } - - em = BKE_editmesh_from_object(obedit); - ret = EDBM_uv_check(em); - - if (ret) { - ARegion *ar = CTX_wm_region(C); - if ((!toolsettings->use_uv_sculpt) || (check_region && ar && (ar->regiontype != RGN_TYPE_WINDOW))) { - ret = 0; - } - } - - return ret; + BMEditMesh *em; + int ret; + Object *obedit = CTX_data_edit_object(C); + SpaceImage *sima = CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = scene->toolsettings; + + if (!uv_sculpt_brush(C) || !obedit || obedit->type != OB_MESH || !sima || + ED_space_image_show_render(sima) || (sima->mode == SI_MODE_PAINT)) { + return 0; + } + + em = BKE_editmesh_from_object(obedit); + ret = EDBM_uv_check(em); + + if (ret) { + ARegion *ar = CTX_wm_region(C); + if ((!toolsettings->use_uv_sculpt) || + (check_region && ar && (ar->regiontype != RGN_TYPE_WINDOW))) { + ret = 0; + } + } + + return ret; } static bool uv_sculpt_brush_poll(bContext *C) { - return uv_sculpt_brush_poll_do(C, true); + return uv_sculpt_brush_poll_do(C, true); } static void brush_drawcursor_uvsculpt(bContext *C, int x, int y, void *UNUSED(customdata)) @@ -191,74 +187,70 @@ static void brush_drawcursor_uvsculpt(bContext *C, int x, int y, void *UNUSED(cu #define PX_SIZE_FADE_MAX 12.0f #define PX_SIZE_FADE_MIN 4.0f - Scene *scene = CTX_data_scene(C); - //Brush *brush = image_paint_brush(C); - Paint *paint = BKE_paint_get_active_from_context(C); - Brush *brush = BKE_paint_brush(paint); - - if (paint && brush && paint->flags & PAINT_SHOW_BRUSH) { - const float size = (float)BKE_brush_size_get(scene, brush); - float alpha = 0.5f; - - /* fade out the brush (cheap trick to work around brush interfering with sampling [#])*/ - if (size < PX_SIZE_FADE_MIN) { - return; - } - else if (size < PX_SIZE_FADE_MAX) { - alpha *= (size - PX_SIZE_FADE_MIN) / (PX_SIZE_FADE_MAX - PX_SIZE_FADE_MIN); - } - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor3fvAlpha(brush->add_col, alpha); - - GPU_line_smooth(true); - GPU_blend(true); - imm_draw_circle_wire_2d(pos, (float)x, (float)y, size, 40); - GPU_blend(false); - GPU_line_smooth(false); - - immUnbindProgram(); - } + Scene *scene = CTX_data_scene(C); + //Brush *brush = image_paint_brush(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + + if (paint && brush && paint->flags & PAINT_SHOW_BRUSH) { + const float size = (float)BKE_brush_size_get(scene, brush); + float alpha = 0.5f; + + /* fade out the brush (cheap trick to work around brush interfering with sampling [#])*/ + if (size < PX_SIZE_FADE_MIN) { + return; + } + else if (size < PX_SIZE_FADE_MAX) { + alpha *= (size - PX_SIZE_FADE_MIN) / (PX_SIZE_FADE_MAX - PX_SIZE_FADE_MIN); + } + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(brush->add_col, alpha); + + GPU_line_smooth(true); + GPU_blend(true); + imm_draw_circle_wire_2d(pos, (float)x, (float)y, size, 40); + GPU_blend(false); + GPU_line_smooth(false); + + immUnbindProgram(); + } #undef PX_SIZE_FADE_MAX #undef PX_SIZE_FADE_MIN } - void ED_space_image_uv_sculpt_update(Main *bmain, wmWindowManager *wm, Scene *scene) { - ToolSettings *settings = scene->toolsettings; - if (settings->use_uv_sculpt) { - if (settings->uvsculpt == NULL) { - settings->uv_sculpt_tool = UV_SCULPT_TOOL_GRAB; - settings->uv_sculpt_settings = UV_SCULPT_LOCK_BORDERS | UV_SCULPT_ALL_ISLANDS; - settings->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN; - } - BKE_paint_ensure(settings, (Paint **)&settings->uvsculpt); - BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT_UV, PAINT_CURSOR_SCULPT); - - settings->uvsculpt->paint.paint_cursor = WM_paint_cursor_activate( - wm, - SPACE_IMAGE, RGN_TYPE_WINDOW, - uv_sculpt_brush_poll, - brush_drawcursor_uvsculpt, NULL); - } - else { - if (settings->uvsculpt) { - WM_paint_cursor_end(wm, settings->uvsculpt->paint.paint_cursor); - settings->uvsculpt->paint.paint_cursor = NULL; - } - } + ToolSettings *settings = scene->toolsettings; + if (settings->use_uv_sculpt) { + if (settings->uvsculpt == NULL) { + settings->uv_sculpt_tool = UV_SCULPT_TOOL_GRAB; + settings->uv_sculpt_settings = UV_SCULPT_LOCK_BORDERS | UV_SCULPT_ALL_ISLANDS; + settings->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN; + } + BKE_paint_ensure(settings, (Paint **)&settings->uvsculpt); + BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT_UV, PAINT_CURSOR_SCULPT); + + settings->uvsculpt->paint.paint_cursor = WM_paint_cursor_activate( + wm, SPACE_IMAGE, RGN_TYPE_WINDOW, uv_sculpt_brush_poll, brush_drawcursor_uvsculpt, NULL); + } + else { + if (settings->uvsculpt) { + WM_paint_cursor_end(wm, settings->uvsculpt->paint.paint_cursor); + settings->uvsculpt->paint.paint_cursor = NULL; + } + } } bool uv_sculpt_poll(bContext *C) { - return uv_sculpt_brush_poll_do(C, true); + return uv_sculpt_brush_poll_do(C, true); } bool uv_sculpt_keymap_poll(bContext *C) { - return uv_sculpt_brush_poll_do(C, false); + return uv_sculpt_brush_poll_do(C, false); } /*********** Improved Laplacian Relaxation Operator ************************/ @@ -267,660 +259,692 @@ bool uv_sculpt_keymap_poll(bContext *C) ***************************************************************************/ typedef struct Temp_UvData { - float sum_co[2], p[2], b[2], sum_b[2]; - int ncounter; + float sum_co[2], p[2], b[2], sum_b[2]; + int ncounter; } Temp_UVData; - - -static void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, float mouse_coord[2], - float alpha, float radius, float aspectRatio) +static void HC_relaxation_iteration_uv(BMEditMesh *em, + UvSculptData *sculptdata, + float mouse_coord[2], + float alpha, + float radius, + float aspectRatio) { - Temp_UVData *tmp_uvdata; - float diff[2]; - int i; - float radius_root = sqrtf(radius); - Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); - - tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); - - /* counting neighbors */ - for (i = 0; i < sculptdata->totalUvEdges; i++) { - UvEdge *tmpedge = sculptdata->uvedges + i; - tmp_uvdata[tmpedge->uv1].ncounter++; - tmp_uvdata[tmpedge->uv2].ncounter++; - - add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); - add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); - } - - for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - copy_v2_v2(diff, tmp_uvdata[i].sum_co); - mul_v2_fl(diff, 1.f / tmp_uvdata[i].ncounter); - copy_v2_v2(tmp_uvdata[i].p, diff); - - tmp_uvdata[i].b[0] = diff[0] - sculptdata->uv[i].uv[0]; - tmp_uvdata[i].b[1] = diff[1] - sculptdata->uv[i].uv[1]; - } - - for (i = 0; i < sculptdata->totalUvEdges; i++) { - UvEdge *tmpedge = sculptdata->uvedges + i; - add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_b, tmp_uvdata[tmpedge->uv2].b); - add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_b, tmp_uvdata[tmpedge->uv1].b); - } - - for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - float dist; - /* 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)) { - continue; - } - - sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); - diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { - UvElement *element; - float strength; - strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); - - sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] + strength * (tmp_uvdata[i].p[0] - 0.5f * (tmp_uvdata[i].b[0] + tmp_uvdata[i].sum_b[0] / tmp_uvdata[i].ncounter)); - sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + strength * (tmp_uvdata[i].p[1] - 0.5f * (tmp_uvdata[i].b[1] + tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter)); - - for (element = sculptdata->uv[i].element; element; element = element->next) { - MLoopUV *luv; - BMLoop *l; - - if (element->separate && element != sculptdata->uv[i].element) - break; - - l = element->l; - luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); - copy_v2_v2(luv->uv, sculptdata->uv[i].uv); - } - } - } - - MEM_freeN(tmp_uvdata); - - return; + Temp_UVData *tmp_uvdata; + float diff[2]; + int i; + float radius_root = sqrtf(radius); + Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); + + tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), + "Temporal data"); + + /* counting neighbors */ + for (i = 0; i < sculptdata->totalUvEdges; i++) { + UvEdge *tmpedge = sculptdata->uvedges + i; + tmp_uvdata[tmpedge->uv1].ncounter++; + tmp_uvdata[tmpedge->uv2].ncounter++; + + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++) { + copy_v2_v2(diff, tmp_uvdata[i].sum_co); + mul_v2_fl(diff, 1.f / tmp_uvdata[i].ncounter); + copy_v2_v2(tmp_uvdata[i].p, diff); + + tmp_uvdata[i].b[0] = diff[0] - sculptdata->uv[i].uv[0]; + tmp_uvdata[i].b[1] = diff[1] - sculptdata->uv[i].uv[1]; + } + + for (i = 0; i < sculptdata->totalUvEdges; i++) { + UvEdge *tmpedge = sculptdata->uvedges + i; + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_b, tmp_uvdata[tmpedge->uv2].b); + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_b, tmp_uvdata[tmpedge->uv1].b); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++) { + float dist; + /* 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)) { + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); + diff[1] /= aspectRatio; + if ((dist = dot_v2v2(diff, diff)) <= radius) { + UvElement *element; + float strength; + strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); + + sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] + + strength * + (tmp_uvdata[i].p[0] - + 0.5f * (tmp_uvdata[i].b[0] + + tmp_uvdata[i].sum_b[0] / tmp_uvdata[i].ncounter)); + sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + + strength * + (tmp_uvdata[i].p[1] - + 0.5f * (tmp_uvdata[i].b[1] + + tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter)); + + for (element = sculptdata->uv[i].element; element; element = element->next) { + MLoopUV *luv; + BMLoop *l; + + if (element->separate && element != sculptdata->uv[i].element) + break; + + l = element->l; + luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); + copy_v2_v2(luv->uv, sculptdata->uv[i].uv); + } + } + } + + MEM_freeN(tmp_uvdata); + + return; } -static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, float mouse_coord[2], float alpha, float radius, float aspectRatio) +static void laplacian_relaxation_iteration_uv(BMEditMesh *em, + UvSculptData *sculptdata, + float mouse_coord[2], + float alpha, + float radius, + float aspectRatio) { - Temp_UVData *tmp_uvdata; - float diff[2]; - int i; - float radius_root = sqrtf(radius); - Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); - - tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); - - /* counting neighbors */ - for (i = 0; i < sculptdata->totalUvEdges; i++) { - UvEdge *tmpedge = sculptdata->uvedges + i; - tmp_uvdata[tmpedge->uv1].ncounter++; - tmp_uvdata[tmpedge->uv2].ncounter++; - - add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); - add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); - } - - /* Original Lacplacian algorithm included removal of normal component of translation. here it is not - * needed since we translate along the UV plane always.*/ - for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co); - mul_v2_fl(tmp_uvdata[i].p, 1.f / tmp_uvdata[i].ncounter); - } - - for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - float dist; - /* 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)) { - continue; - } - - sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); - diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { - UvElement *element; - float strength; - strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); - - sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] + strength * tmp_uvdata[i].p[0]; - sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + strength * tmp_uvdata[i].p[1]; - - for (element = sculptdata->uv[i].element; element; element = element->next) { - MLoopUV *luv; - BMLoop *l; - - if (element->separate && element != sculptdata->uv[i].element) - break; - - l = element->l; - luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); - copy_v2_v2(luv->uv, sculptdata->uv[i].uv); - } - } - } - - MEM_freeN(tmp_uvdata); - - return; + Temp_UVData *tmp_uvdata; + float diff[2]; + int i; + float radius_root = sqrtf(radius); + Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); + + tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), + "Temporal data"); + + /* counting neighbors */ + for (i = 0; i < sculptdata->totalUvEdges; i++) { + UvEdge *tmpedge = sculptdata->uvedges + i; + tmp_uvdata[tmpedge->uv1].ncounter++; + tmp_uvdata[tmpedge->uv2].ncounter++; + + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } + + /* Original Lacplacian algorithm included removal of normal component of translation. here it is not + * needed since we translate along the UV plane always.*/ + for (i = 0; i < sculptdata->totalUniqueUvs; i++) { + copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co); + mul_v2_fl(tmp_uvdata[i].p, 1.f / tmp_uvdata[i].ncounter); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++) { + float dist; + /* 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)) { + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); + diff[1] /= aspectRatio; + if ((dist = dot_v2v2(diff, diff)) <= radius) { + UvElement *element; + float strength; + strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); + + sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] + + strength * tmp_uvdata[i].p[0]; + sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + + strength * tmp_uvdata[i].p[1]; + + for (element = sculptdata->uv[i].element; element; element = element->next) { + MLoopUV *luv; + BMLoop *l; + + if (element->separate && element != sculptdata->uv[i].element) + break; + + l = element->l; + luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); + copy_v2_v2(luv->uv, sculptdata->uv[i].uv); + } + } + } + + MEM_freeN(tmp_uvdata); + + return; } - -static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, const wmEvent *event, Object *obedit) +static void uv_sculpt_stroke_apply(bContext *C, + wmOperator *op, + const wmEvent *event, + Object *obedit) { - float co[2], radius, radius_root; - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - unsigned int tool; - UvSculptData *sculptdata = (UvSculptData *)op->customdata; - SpaceImage *sima; - int invert; - int width, height; - float aspectRatio; - float alpha, zoomx, zoomy; - Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); - ToolSettings *toolsettings = CTX_data_tool_settings(C); - tool = sculptdata->tool; - invert = sculptdata->invert ? -1 : 1; - alpha = BKE_brush_alpha_get(scene, brush); - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); - - sima = CTX_wm_space_image(C); - ED_space_image_get_size(sima, &width, &height); - ED_space_image_get_zoom(sima, ar, &zoomx, &zoomy); - - radius = BKE_brush_size_get(scene, brush) / (width * zoomx); - aspectRatio = width / (float)height; - - /* We will compare squares to save some computation */ - radius = radius * radius; - radius_root = sqrtf(radius); - - /* - * Pinch Tool - */ - if (tool == UV_SCULPT_TOOL_PINCH) { - int i; - alpha *= invert; - for (i = 0; i < sculptdata->totalUniqueUvs; i++) { - float dist, diff[2]; - /* This is supposed to happen only if "Lock Borders" 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) { - continue; - } - - sub_v2_v2v2(diff, sculptdata->uv[i].uv, co); - diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { - UvElement *element; - float strength; - strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); - normalize_v2(diff); - - sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f; - sculptdata->uv[i].uv[1] -= strength * diff[1] * 0.001f; - - for (element = sculptdata->uv[i].element; element; element = element->next) { - MLoopUV *luv; - BMLoop *l; - - if (element->separate && element != sculptdata->uv[i].element) - break; - - l = element->l; - luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); - copy_v2_v2(luv->uv, sculptdata->uv[i].uv); - } - } - } - } - - /* - * Smooth Tool - */ - else if (tool == UV_SCULPT_TOOL_RELAX) { - unsigned int method = toolsettings->uv_relax_method; - if (method == UV_SCULPT_TOOL_RELAX_HC) { - HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); - } - else { - laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); - } - } - - /* - * Grab Tool - */ - else if (tool == UV_SCULPT_TOOL_GRAB) { - int i; - float diff[2]; - sub_v2_v2v2(diff, co, sculptdata->initial_stroke->init_coord); - - for (i = 0; i < sculptdata->initial_stroke->totalInitialSelected; i++) { - UvElement *element; - int uvindex = sculptdata->initial_stroke->initialSelection[i].uv; - float strength = sculptdata->initial_stroke->initialSelection[i].strength; - sculptdata->uv[uvindex].uv[0] = sculptdata->initial_stroke->initialSelection[i].initial_uv[0] + strength * diff[0]; - sculptdata->uv[uvindex].uv[1] = sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength * diff[1]; - - for (element = sculptdata->uv[uvindex].element; element; element = element->next) { - MLoopUV *luv; - BMLoop *l; - - if (element->separate && element != sculptdata->uv[uvindex].element) - break; - - l = element->l; - luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); - copy_v2_v2(luv->uv, sculptdata->uv[uvindex].uv); - } - } - } + float co[2], radius, radius_root; + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + unsigned int tool; + UvSculptData *sculptdata = (UvSculptData *)op->customdata; + SpaceImage *sima; + int invert; + int width, height; + float aspectRatio; + float alpha, zoomx, zoomy; + Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + tool = sculptdata->tool; + invert = sculptdata->invert ? -1 : 1; + alpha = BKE_brush_alpha_get(scene, brush); + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + sima = CTX_wm_space_image(C); + ED_space_image_get_size(sima, &width, &height); + ED_space_image_get_zoom(sima, ar, &zoomx, &zoomy); + + radius = BKE_brush_size_get(scene, brush) / (width * zoomx); + aspectRatio = width / (float)height; + + /* We will compare squares to save some computation */ + radius = radius * radius; + radius_root = sqrtf(radius); + + /* + * Pinch Tool + */ + if (tool == UV_SCULPT_TOOL_PINCH) { + int i; + alpha *= invert; + for (i = 0; i < sculptdata->totalUniqueUvs; i++) { + float dist, diff[2]; + /* This is supposed to happen only if "Lock Borders" 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) { + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, co); + diff[1] /= aspectRatio; + if ((dist = dot_v2v2(diff, diff)) <= radius) { + UvElement *element; + float strength; + strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); + normalize_v2(diff); + + sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f; + sculptdata->uv[i].uv[1] -= strength * diff[1] * 0.001f; + + for (element = sculptdata->uv[i].element; element; element = element->next) { + MLoopUV *luv; + BMLoop *l; + + if (element->separate && element != sculptdata->uv[i].element) + break; + + l = element->l; + luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); + copy_v2_v2(luv->uv, sculptdata->uv[i].uv); + } + } + } + } + + /* + * Smooth Tool + */ + else if (tool == UV_SCULPT_TOOL_RELAX) { + unsigned int method = toolsettings->uv_relax_method; + if (method == UV_SCULPT_TOOL_RELAX_HC) { + HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); + } + else { + laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); + } + } + + /* + * Grab Tool + */ + else if (tool == UV_SCULPT_TOOL_GRAB) { + int i; + float diff[2]; + sub_v2_v2v2(diff, co, sculptdata->initial_stroke->init_coord); + + for (i = 0; i < sculptdata->initial_stroke->totalInitialSelected; i++) { + UvElement *element; + int uvindex = sculptdata->initial_stroke->initialSelection[i].uv; + float strength = sculptdata->initial_stroke->initialSelection[i].strength; + sculptdata->uv[uvindex].uv[0] = + sculptdata->initial_stroke->initialSelection[i].initial_uv[0] + strength * diff[0]; + sculptdata->uv[uvindex].uv[1] = + sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength * diff[1]; + + for (element = sculptdata->uv[uvindex].element; element; element = element->next) { + MLoopUV *luv; + BMLoop *l; + + if (element->separate && element != sculptdata->uv[uvindex].element) + break; + + l = element->l; + luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); + copy_v2_v2(luv->uv, sculptdata->uv[uvindex].uv); + } + } + } } - static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) { - UvSculptData *data = op->customdata; - if (data->timer) { - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); - } - if (data->elementMap) { - BM_uv_element_map_free(data->elementMap); - } - if (data->uv) { - MEM_freeN(data->uv); - } - if (data->uvedges) { - MEM_freeN(data->uvedges); - } - if (data->initial_stroke) { - if (data->initial_stroke->initialSelection) { - MEM_freeN(data->initial_stroke->initialSelection); - } - MEM_freeN(data->initial_stroke); - } - - MEM_freeN(data); - op->customdata = NULL; + UvSculptData *data = op->customdata; + if (data->timer) { + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); + } + if (data->elementMap) { + BM_uv_element_map_free(data->elementMap); + } + if (data->uv) { + MEM_freeN(data->uv); + } + if (data->uvedges) { + MEM_freeN(data->uvedges); + } + if (data->initial_stroke) { + if (data->initial_stroke->initialSelection) { + MEM_freeN(data->initial_stroke->initialSelection); + } + MEM_freeN(data->initial_stroke); + } + + MEM_freeN(data); + op->customdata = NULL; } -static int uv_element_offset_from_face_get(UvElementMap *map, BMFace *efa, BMLoop *l, int island_index, const bool doIslands) +static int uv_element_offset_from_face_get( + UvElementMap *map, BMFace *efa, BMLoop *l, int island_index, const bool doIslands) { - UvElement *element = BM_uv_element_get(map, efa, l); - if (!element || (doIslands && element->island != island_index)) { - return -1; - } - return element - map->buf; + UvElement *element = BM_uv_element_get(map, efa, l); + if (!element || (doIslands && element->island != island_index)) { + return -1; + } + return element - map->buf; } - static unsigned int uv_edge_hash(const void *key) { - const UvEdge *edge = key; - return (BLI_ghashutil_uinthash(edge->uv2) + - BLI_ghashutil_uinthash(edge->uv1)); + const UvEdge *edge = key; + return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1)); } static bool uv_edge_compare(const void *a, const void *b) { - const UvEdge *edge1 = a; - const UvEdge *edge2 = b; + const UvEdge *edge1 = a; + const UvEdge *edge2 = b; - if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) { - return 0; - } - return 1; + if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) { + return 0; + } + return 1; } - static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event) { - Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - ToolSettings *ts = scene->toolsettings; - UvSculptData *data = MEM_callocN(sizeof(*data), "UV Smooth Brush Data"); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - - op->customdata = data; - - curvemapping_initialize(ts->uvsculpt->paint.brush->curve); - - if (data) { - int counter = 0, i; - ARegion *ar = CTX_wm_region(C); - float co[2]; - BMFace *efa; - MLoopUV *luv; - BMLoop *l; - BMIter iter, liter; - - UvEdge *edges; - GHash *edgeHash; - GHashIterator gh_iter; - - bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); - int island_index = 0; - /* Holds, for each UvElement in elementMap, a pointer to its unique uv.*/ - int *uniqueUv; - data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ? UV_SCULPT_TOOL_RELAX : ts->uv_sculpt_tool; - data->invert = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT) ? 1 : 0; - - data->uvsculpt = &ts->uvsculpt->paint; - - if (do_island_optimization) { - /* We will need island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, true, true, true); - } - } - else { - if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, false, true, false); - } - else { - data->elementMap = BM_uv_element_map_create(bm, true, true, false); - } - } - - if (!data->elementMap) { - uv_sculpt_stroke_exit(C, op); - return NULL; - } - - /* Mouse coordinates, useful for some functions like grab and sculpt all islands */ - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); - - /* we need to find the active island here */ - if (do_island_optimization) { - UvElement *element; - UvNearestHit hit = UV_NEAREST_HIT_INIT; - Image *ima = CTX_data_edit_image(C); - uv_find_nearest_vert(scene, ima, obedit, co, 0.0f, &hit); - - element = BM_uv_element_get(data->elementMap, hit.efa, hit.l); - island_index = element->island; - } - - - /* Count 'unique' uvs */ - for (i = 0; i < data->elementMap->totalUVs; i++) { - if (data->elementMap->buf[i].separate && - (!do_island_optimization || data->elementMap->buf[i].island == island_index)) - { - counter++; - } - } - - /* Allocate the unique uv buffers */ - data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs"); - uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs, "uv_brush_unique_uv_map"); - edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); - /* we have at most totalUVs edges */ - edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges"); - if (!data->uv || !uniqueUv || !edgeHash || !edges) { - if (edges) { - MEM_freeN(edges); - } - if (uniqueUv) { - MEM_freeN(uniqueUv); - } - if (edgeHash) { - BLI_ghash_free(edgeHash, NULL, NULL); - } - uv_sculpt_stroke_exit(C, op); - return NULL; - } - - data->totalUniqueUvs = counter; - /* So that we can use this as index for the UvElements */ - counter = -1; - /* initialize the unique UVs */ - for (i = 0; i < bm->totvert; i++) { - UvElement *element = data->elementMap->vert[i]; - for (; element; element = element->next) { - if (element->separate) { - if (do_island_optimization && (element->island != island_index)) { - /* skip this uv if not on the active island */ - for (; element->next && !(element->next->separate); element = element->next) - ; - continue; - } - - l = element->l; - luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); - - counter++; - data->uv[counter].element = element; - data->uv[counter].flag = 0; - data->uv[counter].uv = luv->uv; - } - /* pointer arithmetic to the rescue, as always :)*/ - uniqueUv[element - data->elementMap->buf] = counter; - } - } - - - /* Now, on to generate our uv connectivity data */ - counter = 0; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - int offset1, itmp1 = uv_element_offset_from_face_get(data->elementMap, efa, l, island_index, do_island_optimization); - int offset2, itmp2 = uv_element_offset_from_face_get(data->elementMap, efa, l->next, island_index, do_island_optimization); - char *flag; - - /* Skip edge if not found(unlikely) or not on valid island */ - if (itmp1 == -1 || itmp2 == -1) - continue; - - offset1 = uniqueUv[itmp1]; - offset2 = uniqueUv[itmp2]; - - edges[counter].flag = 0; - /* using an order policy, sort uvs according to address space. This avoids - * Having two different UvEdges with the same uvs on different positions */ - if (offset1 < offset2) { - edges[counter].uv1 = offset1; - edges[counter].uv2 = offset2; - } - else { - edges[counter].uv1 = offset2; - edges[counter].uv2 = offset1; - } - /* Hack! Set the value of the key to its flag. - * Now we can set the flag when an edge exists twice :) */ - flag = BLI_ghash_lookup(edgeHash, &edges[counter]); - if (flag) { - *flag = 1; - } - else { - /* Hack mentioned */ - BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag); - } - counter++; - } - } - - MEM_freeN(uniqueUv); - - /* Allocate connectivity data, we allocate edges once */ - data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash), "uv_brush_edge_connectivity_data"); - if (!data->uvedges) { - BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); - uv_sculpt_stroke_exit(C, op); - return NULL; - } - - /* fill the edges with data */ - i = 0; - GHASH_ITER (gh_iter, edgeHash) { - data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); - } - data->totalUvEdges = BLI_ghash_len(edgeHash); - - /* cleanup temporary stuff */ - BLI_ghash_free(edgeHash, NULL, NULL); - MEM_freeN(edges); - - /* transfer boundary edge property to uvs */ - if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { - for (i = 0; i < data->totalUvEdges; i++) { - if (!data->uvedges[i].flag) { - data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; - data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; - } - } - } - - /* Allocate initial selection for grab tool */ - if (data->tool == UV_SCULPT_TOOL_GRAB) { - float radius, radius_root; - UvSculptData *sculptdata = (UvSculptData *)op->customdata; - SpaceImage *sima; - int width, height; - float aspectRatio; - float alpha, zoomx, zoomy; - Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); - - alpha = BKE_brush_alpha_get(scene, brush); - - radius = BKE_brush_size_get(scene, brush); - sima = CTX_wm_space_image(C); - ED_space_image_get_size(sima, &width, &height); - ED_space_image_get_zoom(sima, ar, &zoomx, &zoomy); - - aspectRatio = width / (float)height; - radius /= (width * zoomx); - radius = radius * radius; - radius_root = sqrtf(radius); - - /* Allocate selection stack */ - data->initial_stroke = MEM_mallocN(sizeof(*data->initial_stroke), "uv_sculpt_initial_stroke"); - if (!data->initial_stroke) { - uv_sculpt_stroke_exit(C, op); - } - data->initial_stroke->initialSelection = MEM_mallocN(sizeof(*data->initial_stroke->initialSelection) * data->totalUniqueUvs, "uv_sculpt_initial_selection"); - if (!data->initial_stroke->initialSelection) { - uv_sculpt_stroke_exit(C, op); - } - - copy_v2_v2(data->initial_stroke->init_coord, co); - - counter = 0; - - for (i = 0; i < data->totalUniqueUvs; i++) { - float dist, diff[2]; - if (data->uv[i].flag & MARK_BOUNDARY) { - continue; - } - - sub_v2_v2v2(diff, data->uv[i].uv, co); - diff[1] /= aspectRatio; - if ((dist = dot_v2v2(diff, diff)) <= radius) { - float strength; - strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); - - data->initial_stroke->initialSelection[counter].uv = i; - data->initial_stroke->initialSelection[counter].strength = strength; - copy_v2_v2(data->initial_stroke->initialSelection[counter].initial_uv, data->uv[i].uv); - counter++; - } - } - - data->initial_stroke->totalInitialSelected = counter; - } - } - - return op->customdata; + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = scene->toolsettings; + UvSculptData *data = MEM_callocN(sizeof(*data), "UV Smooth Brush Data"); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + op->customdata = data; + + curvemapping_initialize(ts->uvsculpt->paint.brush->curve); + + if (data) { + int counter = 0, i; + ARegion *ar = CTX_wm_region(C); + float co[2]; + BMFace *efa; + MLoopUV *luv; + BMLoop *l; + BMIter iter, liter; + + UvEdge *edges; + GHash *edgeHash; + GHashIterator gh_iter; + + bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); + int island_index = 0; + /* Holds, for each UvElement in elementMap, a pointer to its unique uv.*/ + int *uniqueUv; + data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ? UV_SCULPT_TOOL_RELAX : + ts->uv_sculpt_tool; + data->invert = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_INVERT) ? 1 : 0; + + data->uvsculpt = &ts->uvsculpt->paint; + + if (do_island_optimization) { + /* We will need island information */ + if (ts->uv_flag & UV_SYNC_SELECTION) { + data->elementMap = BM_uv_element_map_create(bm, false, true, true); + } + else { + data->elementMap = BM_uv_element_map_create(bm, true, true, true); + } + } + else { + if (ts->uv_flag & UV_SYNC_SELECTION) { + data->elementMap = BM_uv_element_map_create(bm, false, true, false); + } + else { + data->elementMap = BM_uv_element_map_create(bm, true, true, false); + } + } + + if (!data->elementMap) { + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + /* Mouse coordinates, useful for some functions like grab and sculpt all islands */ + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + /* we need to find the active island here */ + if (do_island_optimization) { + UvElement *element; + UvNearestHit hit = UV_NEAREST_HIT_INIT; + Image *ima = CTX_data_edit_image(C); + uv_find_nearest_vert(scene, ima, obedit, co, 0.0f, &hit); + + element = BM_uv_element_get(data->elementMap, hit.efa, hit.l); + island_index = element->island; + } + + /* Count 'unique' uvs */ + for (i = 0; i < data->elementMap->totalUVs; i++) { + if (data->elementMap->buf[i].separate && + (!do_island_optimization || data->elementMap->buf[i].island == island_index)) { + counter++; + } + } + + /* Allocate the unique uv buffers */ + data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs"); + uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs, + "uv_brush_unique_uv_map"); + edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); + /* we have at most totalUVs edges */ + edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges"); + if (!data->uv || !uniqueUv || !edgeHash || !edges) { + if (edges) { + MEM_freeN(edges); + } + if (uniqueUv) { + MEM_freeN(uniqueUv); + } + if (edgeHash) { + BLI_ghash_free(edgeHash, NULL, NULL); + } + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + data->totalUniqueUvs = counter; + /* So that we can use this as index for the UvElements */ + counter = -1; + /* initialize the unique UVs */ + for (i = 0; i < bm->totvert; i++) { + UvElement *element = data->elementMap->vert[i]; + for (; element; element = element->next) { + if (element->separate) { + if (do_island_optimization && (element->island != island_index)) { + /* skip this uv if not on the active island */ + for (; element->next && !(element->next->separate); element = element->next) + ; + continue; + } + + l = element->l; + luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); + + counter++; + data->uv[counter].element = element; + data->uv[counter].flag = 0; + data->uv[counter].uv = luv->uv; + } + /* pointer arithmetic to the rescue, as always :)*/ + uniqueUv[element - data->elementMap->buf] = counter; + } + } + + /* Now, on to generate our uv connectivity data */ + counter = 0; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + int offset1, itmp1 = uv_element_offset_from_face_get( + data->elementMap, efa, l, island_index, do_island_optimization); + int offset2, itmp2 = uv_element_offset_from_face_get( + data->elementMap, efa, l->next, island_index, do_island_optimization); + char *flag; + + /* Skip edge if not found(unlikely) or not on valid island */ + if (itmp1 == -1 || itmp2 == -1) + continue; + + offset1 = uniqueUv[itmp1]; + offset2 = uniqueUv[itmp2]; + + edges[counter].flag = 0; + /* using an order policy, sort uvs according to address space. This avoids + * Having two different UvEdges with the same uvs on different positions */ + if (offset1 < offset2) { + edges[counter].uv1 = offset1; + edges[counter].uv2 = offset2; + } + else { + edges[counter].uv1 = offset2; + edges[counter].uv2 = offset1; + } + /* Hack! Set the value of the key to its flag. + * Now we can set the flag when an edge exists twice :) */ + flag = BLI_ghash_lookup(edgeHash, &edges[counter]); + if (flag) { + *flag = 1; + } + else { + /* Hack mentioned */ + BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag); + } + counter++; + } + } + + MEM_freeN(uniqueUv); + + /* Allocate connectivity data, we allocate edges once */ + data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash), + "uv_brush_edge_connectivity_data"); + if (!data->uvedges) { + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + /* fill the edges with data */ + i = 0; + GHASH_ITER (gh_iter, edgeHash) { + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + } + data->totalUvEdges = BLI_ghash_len(edgeHash); + + /* cleanup temporary stuff */ + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + + /* transfer boundary edge property to uvs */ + if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { + for (i = 0; i < data->totalUvEdges; i++) { + if (!data->uvedges[i].flag) { + data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; + data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; + } + } + } + + /* Allocate initial selection for grab tool */ + if (data->tool == UV_SCULPT_TOOL_GRAB) { + float radius, radius_root; + UvSculptData *sculptdata = (UvSculptData *)op->customdata; + SpaceImage *sima; + int width, height; + float aspectRatio; + float alpha, zoomx, zoomy; + Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); + + alpha = BKE_brush_alpha_get(scene, brush); + + radius = BKE_brush_size_get(scene, brush); + sima = CTX_wm_space_image(C); + ED_space_image_get_size(sima, &width, &height); + ED_space_image_get_zoom(sima, ar, &zoomx, &zoomy); + + aspectRatio = width / (float)height; + radius /= (width * zoomx); + radius = radius * radius; + radius_root = sqrtf(radius); + + /* Allocate selection stack */ + data->initial_stroke = MEM_mallocN(sizeof(*data->initial_stroke), + "uv_sculpt_initial_stroke"); + if (!data->initial_stroke) { + uv_sculpt_stroke_exit(C, op); + } + data->initial_stroke->initialSelection = MEM_mallocN( + sizeof(*data->initial_stroke->initialSelection) * data->totalUniqueUvs, + "uv_sculpt_initial_selection"); + if (!data->initial_stroke->initialSelection) { + uv_sculpt_stroke_exit(C, op); + } + + copy_v2_v2(data->initial_stroke->init_coord, co); + + counter = 0; + + for (i = 0; i < data->totalUniqueUvs; i++) { + float dist, diff[2]; + if (data->uv[i].flag & MARK_BOUNDARY) { + continue; + } + + sub_v2_v2v2(diff, data->uv[i].uv, co); + diff[1] /= aspectRatio; + if ((dist = dot_v2v2(diff, diff)) <= radius) { + float strength; + strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); + + data->initial_stroke->initialSelection[counter].uv = i; + data->initial_stroke->initialSelection[counter].strength = strength; + copy_v2_v2(data->initial_stroke->initialSelection[counter].initial_uv, data->uv[i].uv); + counter++; + } + } + + data->initial_stroke->totalInitialSelected = counter; + } + } + + return op->customdata; } static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - UvSculptData *data; - Object *obedit = CTX_data_edit_object(C); + UvSculptData *data; + Object *obedit = CTX_data_edit_object(C); - if (!(data = uv_sculpt_stroke_init(C, op, event))) { - return OPERATOR_CANCELLED; - } + if (!(data = uv_sculpt_stroke_init(C, op, event))) { + return OPERATOR_CANCELLED; + } - uv_sculpt_stroke_apply(C, op, event, obedit); + uv_sculpt_stroke_apply(C, op, event, obedit); - data->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.001f); + data->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.001f); - if (!data->timer) { - uv_sculpt_stroke_exit(C, op); - return OPERATOR_CANCELLED; - } - WM_event_add_modal_handler(C, op); + if (!data->timer) { + uv_sculpt_stroke_exit(C, op); + return OPERATOR_CANCELLED; + } + WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } - static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) { - UvSculptData *data = (UvSculptData *)op->customdata; - Object *obedit = CTX_data_edit_object(C); - - switch (event->type) { - case LEFTMOUSE: - case MIDDLEMOUSE: - case RIGHTMOUSE: - uv_sculpt_stroke_exit(C, op); - return OPERATOR_FINISHED; - - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - uv_sculpt_stroke_apply(C, op, event, obedit); - break; - case TIMER: - if (event->customdata == data->timer) - uv_sculpt_stroke_apply(C, op, event, obedit); - break; - default: - return OPERATOR_RUNNING_MODAL; - } - - ED_region_tag_redraw(CTX_wm_region(C)); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); - DEG_id_tag_update(obedit->data, 0); - return OPERATOR_RUNNING_MODAL; + UvSculptData *data = (UvSculptData *)op->customdata; + Object *obedit = CTX_data_edit_object(C); + + switch (event->type) { + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: + uv_sculpt_stroke_exit(C, op); + return OPERATOR_FINISHED; + + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + uv_sculpt_stroke_apply(C, op, event, obedit); + break; + case TIMER: + if (event->customdata == data->timer) + uv_sculpt_stroke_apply(C, op, event, obedit); + break; + default: + return OPERATOR_RUNNING_MODAL; + } + + ED_region_tag_redraw(CTX_wm_region(C)); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + DEG_id_tag_update(obedit->data, 0); + return OPERATOR_RUNNING_MODAL; } void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot) { - static const EnumPropertyItem stroke_mode_items[] = { - {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"}, - {BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"}, - {BRUSH_STROKE_SMOOTH, "RELAX", 0, "Relax", "Switch brush to relax mode for duration of stroke"}, - {0}, - }; - - /* identifiers */ - ot->name = "Sculpt UVs"; - ot->description = "Sculpt UVs using a brush"; - ot->idname = "SCULPT_OT_uv_sculpt_stroke"; - - /* api callbacks */ - ot->invoke = uv_sculpt_stroke_invoke; - ot->modal = uv_sculpt_stroke_modal; - ot->poll = uv_sculpt_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* props */ - RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, "Mode", "Stroke Mode"); + static const EnumPropertyItem stroke_mode_items[] = { + {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"}, + {BRUSH_STROKE_INVERT, + "INVERT", + 0, + "Invert", + "Invert action of brush for duration of stroke"}, + {BRUSH_STROKE_SMOOTH, + "RELAX", + 0, + "Relax", + "Switch brush to relax mode for duration of stroke"}, + {0}, + }; + + /* identifiers */ + ot->name = "Sculpt UVs"; + ot->description = "Sculpt UVs using a brush"; + ot->idname = "SCULPT_OT_uv_sculpt_stroke"; + + /* api callbacks */ + ot->invoke = uv_sculpt_stroke_invoke; + ot->modal = uv_sculpt_stroke_modal; + ot->poll = uv_sculpt_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, "Mode", "Stroke Mode"); } |