Project Paint: Add symmetry support

- Access from symmetry panel (as with sculpt)
- Supports multiple axis at once.
- Supports all brush types including clone.
This commit is contained in:
Campbell Barton 2015-04-28 23:34:40 +10:00
parent 43616918f3
commit e2d60d180e
4 changed files with 378 additions and 167 deletions

@ -1698,6 +1698,25 @@ class VIEW3D_PT_tools_imagepaint_external(Panel, View3DPaintPanel):
col.operator("paint.project_image", text="Apply Camera Image") col.operator("paint.project_image", text="Apply Camera Image")
class VIEW3D_PT_tools_imagepaint_symmetry(Panel, View3DPaintPanel):
bl_category = "Tools"
bl_context = "imagepaint"
bl_label = "Symmetry"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
toolsettings = context.tool_settings
ipaint = toolsettings.image_paint
col = layout.column(align=True)
row = col.row(align=True)
row.prop(ipaint, "use_symmetry_x", text="X", toggle=True)
row.prop(ipaint, "use_symmetry_y", text="Y", toggle=True)
row.prop(ipaint, "use_symmetry_z", text="Z", toggle=True)
class VIEW3D_PT_tools_projectpaint(View3DPaintPanel, Panel): class VIEW3D_PT_tools_projectpaint(View3DPaintPanel, Panel):
bl_category = "Options" bl_category = "Options"
bl_context = "imagepaint" bl_context = "imagepaint"

@ -219,6 +219,7 @@ void ED_view3d_win_to_vector(const struct ARegion *ar, const float mval[2], floa
bool ED_view3d_win_to_segment(const struct ARegion *ar, struct View3D *v3d, const float mval[2], bool ED_view3d_win_to_segment(const struct ARegion *ar, struct View3D *v3d, const float mval[2],
float r_ray_start[3], float r_ray_end[3], const bool do_clip); float r_ray_start[3], float r_ray_end[3], const bool do_clip);
void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, struct Object *ob, float pmat[4][4]); void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, struct Object *ob, float pmat[4][4]);
void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, float obmat[4][4], float pmat[4][4]);
void ED_view3d_unproject(struct bglMats *mats, float out[3], const float x, const float y, const float z); void ED_view3d_unproject(struct bglMats *mats, float out[3], const float x, const float y, const float z);
/* end */ /* end */

@ -43,6 +43,7 @@
#include "BLI_blenlib.h" #include "BLI_blenlib.h"
#include "BLI_linklist.h" #include "BLI_linklist.h"
#include "BLI_math.h" #include "BLI_math.h"
#include "BLI_math_bits.h"
#include "BLI_math_color_blend.h" #include "BLI_math_color_blend.h"
#include "BLI_memarena.h" #include "BLI_memarena.h"
#include "BLI_threads.h" #include "BLI_threads.h"
@ -196,6 +197,29 @@ typedef struct ProjPaintImage {
bool touch; bool touch;
} ProjPaintImage; } ProjPaintImage;
/**
* Handle for stroke (operator customdata)
*/
typedef struct ProjStrokeHandle {
/* Support for painting from multiple views at once,
* currently used to impliment summetry 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;
bool need_redraw;
/* trick to bypass regular paint and allow clone picking */
bool is_clone_cursor_pick;
/* In ProjPaintState, only here for convenience */
Scene *scene;
Brush *brush;
} ProjStrokeHandle;
/* Main projection painting struct passed to all projection painting functions */ /* Main projection painting struct passed to all projection painting functions */
typedef struct ProjPaintState { typedef struct ProjPaintState {
View3D *v3d; View3D *v3d;
@ -211,24 +235,14 @@ typedef struct ProjPaintState {
Brush *brush; Brush *brush;
short tool, blend, mode; short tool, blend, mode;
int orig_brush_size;
float brush_size; float brush_size;
Object *ob; Object *ob;
/* for symmetry, we need to store modified object matrix */
float obmat[4][4];
float obmat_imat[4][4];
/* end similarities with ImagePaintState */ /* end similarities with ImagePaintState */
DerivedMesh *dm;
int dm_totface;
int dm_totedge;
int dm_totvert;
int dm_release;
MVert *dm_mvert;
MEdge *dm_medge;
MFace *dm_mface;
MTFace **dm_mtface;
MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */
MTFace *dm_mtface_stencil;
Image *stencil_ima; Image *stencil_ima;
Image *canvas_ima; Image *canvas_ima;
Image *clone_ima; Image *clone_ima;
@ -239,24 +253,16 @@ typedef struct ProjPaintState {
LinkNode **bucketRect; /* screen sized 2D array, each pixel has a linked list of ProjPixel's */ LinkNode **bucketRect; /* screen sized 2D array, each pixel has a linked list of ProjPixel's */
LinkNode **bucketFaces; /* bucketRect aligned array linkList of faces overlapping each bucket */ LinkNode **bucketFaces; /* bucketRect aligned array linkList of faces overlapping each bucket */
unsigned char *bucketFlags; /* store if the bucks have been initialized */ unsigned char *bucketFlags; /* store if the bucks have been initialized */
#ifndef PROJ_DEBUG_NOSEAMBLEED
char *faceSeamFlags; /* store info about faces, if they are initialized etc*/
char *faceWindingFlags; /* save the winding of the face in uv space, helps as an extra validation step for seam detection */
float (*faceSeamUVs)[4][2]; /* expanded UVs for faces to use as seams */
LinkNode **vertFaces; /* Only needed for when seam_bleed_px is enabled, use to find UV seams */
#endif
char *vertFlags; /* store options per vert, now only store if the vert is pointing away from the view */ char *vertFlags; /* store options per vert, now only store if the vert is pointing away from the view */
int buckets_x; /* 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; /* 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_y; int buckets_y;
ProjPaintImage *projImages;
int pixel_sizeof; /* result of project_paint_pixel_sizeof(), constant per stroke */ int pixel_sizeof; /* result of project_paint_pixel_sizeof(), constant per stroke */
int image_tot; /* size of projectImages array */ int image_tot; /* size of projectImages array */
float (*screenCoords)[4]; /* verts projected into floating point screen space */ float (*screenCoords)[4]; /* verts projected into floating point screen space */
float *cavities; /* cavity amount for vertices */
float screenMin[2]; /* 2D bounds for mesh verts on the screen's plane (screenspace) */ float screenMin[2]; /* 2D bounds for mesh verts on the screen's plane (screenspace) */
float screenMax[2]; float screenMax[2];
float screen_width; /* Calculated from screenMin & screenMax */ float screen_width; /* Calculated from screenMin & screenMax */
@ -308,13 +314,51 @@ typedef struct ProjPaintState {
int bucketMax[2]; int bucketMax[2];
int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */ int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */
/* redraw */
bool need_redraw;
struct CurveMapping *cavity_curve; struct CurveMapping *cavity_curve;
BlurKernel *blurkernel; 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_OFS(ps_dst, ps_src, is_shared_user)
#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) \
MEMSET_STRUCT_OFS(ps, 0, is_shared_user)
bool is_shared_user;
ProjPaintImage *projImages;
float *cavities; /* cavity amount for vertices */
#ifndef PROJ_DEBUG_NOSEAMBLEED
char *faceSeamFlags; /* store info about faces, if they are initialized etc*/
char *faceWindingFlags; /* save the winding of the face in uv space, helps as an extra validation step for seam detection */
float (*faceSeamUVs)[4][2]; /* expanded UVs for faces to use as seams */
LinkNode **vertFaces; /* Only needed for when seam_bleed_px is enabled, use to find UV seams */
#endif
SpinLock *tile_lock; SpinLock *tile_lock;
DerivedMesh *dm;
int dm_totface;
int dm_totedge;
int dm_totvert;
int dm_release;
MVert *dm_mvert;
MEdge *dm_medge;
MFace *dm_mface;
MTFace *dm_mtface_stencil;
MTFace **dm_mtface;
MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */
} ProjPaintState; } ProjPaintState;
typedef union pixelPointer { typedef union pixelPointer {
@ -3110,7 +3154,8 @@ static void proj_paint_state_non_cddm_init(ProjPaintState *ps)
} }
} }
static void proj_paint_state_viewport_init(ProjPaintState *ps) static void proj_paint_state_viewport_init(
ProjPaintState *ps, const char symmetry_flag)
{ {
float mat[3][3]; float mat[3][3];
float viewmat[4][4]; float viewmat[4][4];
@ -3120,7 +3165,19 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps)
ps->viewDir[1] = 0.0f; ps->viewDir[1] = 0.0f;
ps->viewDir[2] = 1.0f; ps->viewDir[2] = 1.0f;
invert_m4_m4(ps->ob->imat, ps->ob->obmat); 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)) { if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) {
/* normal drawing */ /* normal drawing */
@ -3130,7 +3187,7 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps)
copy_m4_m4(viewmat, ps->rv3d->viewmat); copy_m4_m4(viewmat, ps->rv3d->viewmat);
copy_m4_m4(viewinv, ps->rv3d->viewinv); copy_m4_m4(viewinv, ps->rv3d->viewinv);
ED_view3d_ob_project_mat_get(ps->rv3d, ps->ob, ps->projectMat); ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat);
ps->is_ortho = ED_view3d_clip_range_get(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend, true); ps->is_ortho = ED_view3d_clip_range_get(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend, true);
} }
@ -3183,16 +3240,15 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps)
} }
/* same as #ED_view3d_ob_project_mat_get */ /* same as #ED_view3d_ob_project_mat_get */
mul_m4_m4m4(vmat, viewmat, ps->ob->obmat); mul_m4_m4m4(vmat, viewmat, ps->obmat);
mul_m4_m4m4(ps->projectMat, winmat, vmat); mul_m4_m4m4(ps->projectMat, winmat, vmat);
} }
/* viewDir - object relative */ /* viewDir - object relative */
invert_m4_m4(ps->ob->imat, ps->ob->obmat);
copy_m3_m4(mat, viewinv); copy_m3_m4(mat, viewinv);
mul_m3_v3(mat, ps->viewDir); mul_m3_v3(mat, ps->viewDir);
copy_m3_m4(mat, ps->ob->imat); copy_m3_m4(mat, ps->obmat_imat);
mul_m3_v3(mat, ps->viewDir); mul_m3_v3(mat, ps->viewDir);
normalize_v3(ps->viewDir); normalize_v3(ps->viewDir);
@ -3202,9 +3258,9 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps)
/* viewPos - object relative */ /* viewPos - object relative */
copy_v3_v3(ps->viewPos, viewinv[3]); copy_v3_v3(ps->viewPos, viewinv[3]);
copy_m3_m4(mat, ps->ob->imat); copy_m3_m4(mat, ps->obmat_imat);
mul_m3_v3(mat, ps->viewPos); mul_m3_v3(mat, ps->viewPos);
add_v3_v3(ps->viewPos, ps->ob->imat[3]); add_v3_v3(ps->viewPos, ps->obmat_imat[3]);
} }
static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter) static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter)
@ -3351,12 +3407,14 @@ static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_th
if (reset_threads) if (reset_threads)
ps->thread_tot = 1; ps->thread_tot = 1;
if (ps->is_shared_user == false) {
if (ps->thread_tot > 1) { if (ps->thread_tot > 1) {
ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock");
BLI_spin_init(ps->tile_lock); BLI_spin_init(ps->tile_lock);
} }
image_undo_init_locks(); image_undo_init_locks();
}
for (a = 0; a < ps->thread_tot; a++) { for (a = 0; a < ps->thread_tot; a++) {
ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena");
@ -3714,7 +3772,8 @@ static void project_paint_prepare_all_faces(
ProjPaintState *ps, MemArena *arena, ProjPaintState *ps, MemArena *arena,
const ProjPaintFaceLookup *face_lookup, const ProjPaintFaceLookup *face_lookup,
ProjPaintLayerClone *layer_clone, ProjPaintLayerClone *layer_clone,
MTFace *tf_base) MTFace *tf_base,
const bool is_multi_view)
{ {
/* Image Vars - keep track of images we have used */ /* Image Vars - keep track of images we have used */
LinkNode *image_LinkList = NULL; LinkNode *image_LinkList = NULL;
@ -3773,6 +3832,7 @@ static void project_paint_prepare_all_faces(
ProjPaintFaceCoSS coSS; ProjPaintFaceCoSS coSS;
proj_paint_face_coSS_init(ps, mf, &coSS); proj_paint_face_coSS_init(ps, mf, &coSS);
if (is_multi_view == false) {
if (project_paint_flt_max_cull(ps, &coSS)) { if (project_paint_flt_max_cull(ps, &coSS)) {
continue; continue;
} }
@ -3784,10 +3844,10 @@ static void project_paint_prepare_all_faces(
#endif //PROJ_DEBUG_WINCLIP #endif //PROJ_DEBUG_WINCLIP
if (project_paint_backface_cull(ps, mf, &coSS)) { if (project_paint_backface_cull(ps, mf, &coSS)) {
continue; continue;
} }
}
if (tpage_last != tpage) { if (tpage_last != tpage) {
@ -3811,14 +3871,18 @@ static void project_paint_prepare_all_faces(
} }
/* build an array of images we use*/ /* build an array of images we use*/
if (ps->is_shared_user == false) {
project_paint_build_proj_ima(ps, arena, image_LinkList); project_paint_build_proj_ima(ps, arena, image_LinkList);
}
/* we have built the array, discard the linked list */ /* we have built the array, discard the linked list */
BLI_linklist_free(image_LinkList, NULL); BLI_linklist_free(image_LinkList, NULL);
} }
/* run once per stroke before projection painting */ /* run once per stroke before projection painting */
static void project_paint_begin(ProjPaintState *ps) static void project_paint_begin(
ProjPaintState *ps,
const bool is_multi_view, const char symmetry_flag)
{ {
ProjPaintLayerClone layer_clone; ProjPaintLayerClone layer_clone;
ProjPaintFaceLookup face_lookup; ProjPaintFaceLookup face_lookup;
@ -3839,9 +3903,11 @@ static void project_paint_begin(ProjPaintState *ps)
ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0; ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0;
/* paint onto the derived mesh */ /* paint onto the derived mesh */
if (ps->is_shared_user == false) {
if (!proj_paint_state_dm_init(ps)) { if (!proj_paint_state_dm_init(ps)) {
return; return;
} }
}
proj_paint_face_lookup_init(ps, &face_lookup); proj_paint_face_lookup_init(ps, &face_lookup);
proj_paint_layer_clone_init(ps, &layer_clone); proj_paint_layer_clone_init(ps, &layer_clone);
@ -3862,11 +3928,13 @@ static void project_paint_begin(ProjPaintState *ps)
} }
/* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ /* 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_non_cddm_init(ps); proj_paint_state_non_cddm_init(ps);
proj_paint_state_cavity_init(ps); proj_paint_state_cavity_init(ps);
}
proj_paint_state_viewport_init(ps); proj_paint_state_viewport_init(ps, symmetry_flag);
/* calculate vert screen coords /* calculate vert screen coords
* run this early so we can calculate the x/y resolution of our bucket rect */ * run this early so we can calculate the x/y resolution of our bucket rect */
@ -3895,7 +3963,9 @@ static void project_paint_begin(ProjPaintState *ps)
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 #ifndef PROJ_DEBUG_NOSEAMBLEED
if (ps->is_shared_user == false) {
proj_paint_state_seam_bleed_init(ps); proj_paint_state_seam_bleed_init(ps);
}
#endif #endif
proj_paint_state_thread_init(ps, reset_threads); proj_paint_state_thread_init(ps, reset_threads);
@ -3903,7 +3973,7 @@ static void project_paint_begin(ProjPaintState *ps)
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, tf_base); project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, tf_base, is_multi_view);
} }
static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2])
@ -3912,7 +3982,7 @@ static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2])
if (ps->tool == PAINT_TOOL_CLONE) { if (ps->tool == PAINT_TOOL_CLONE) {
float projCo[4]; float projCo[4];
copy_v3_v3(projCo, ED_view3d_cursor3d_get(ps->scene, ps->v3d)); copy_v3_v3(projCo, ED_view3d_cursor3d_get(ps->scene, ps->v3d));
mul_m4_v3(ps->ob->imat, projCo); mul_m4_v3(ps->obmat_imat, projCo);
projCo[3] = 1.0f; projCo[3] = 1.0f;
mul_m4_v4(ps->projectMat, projCo); mul_m4_v4(ps->projectMat, projCo);
@ -3924,15 +3994,17 @@ static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2])
static void project_paint_end(ProjPaintState *ps) static void project_paint_end(ProjPaintState *ps)
{ {
int a; int a;
ProjPaintImage *projIma;
image_undo_remove_masks(); image_undo_remove_masks();
/* dereference used image buffers */ /* dereference used image buffers */
if (ps->is_shared_user == false) {
ProjPaintImage *projIma;
for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) {
BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL);
DAG_id_tag_update(&projIma->ima->id, 0); DAG_id_tag_update(&projIma->ima->id, 0);
} }
}
BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL);
@ -3940,13 +4012,21 @@ static void project_paint_end(ProjPaintState *ps)
MEM_freeN(ps->bucketRect); MEM_freeN(ps->bucketRect);
MEM_freeN(ps->bucketFaces); MEM_freeN(ps->bucketFaces);
MEM_freeN(ps->bucketFlags); MEM_freeN(ps->bucketFlags);
if (ps->is_shared_user == false) {
/* must be set for non-shared */
BLI_assert(ps->dm_mtface || ps->is_shared_user);
if (ps->dm_mtface)
MEM_freeN(ps->dm_mtface); MEM_freeN(ps->dm_mtface);
if (ps->do_layer_clone) if (ps->do_layer_clone)
MEM_freeN(ps->dm_mtface_clone); MEM_freeN(ps->dm_mtface_clone);
if (ps->thread_tot > 1) { if (ps->thread_tot > 1) {
BLI_spin_end(ps->tile_lock); BLI_spin_end(ps->tile_lock);
MEM_freeN((void *)ps->tile_lock); MEM_freeN((void *)ps->tile_lock);
} }
image_undo_end_locks(); image_undo_end_locks();
#ifndef PROJ_DEBUG_NOSEAMBLEED #ifndef PROJ_DEBUG_NOSEAMBLEED
@ -3958,21 +4038,10 @@ static void project_paint_end(ProjPaintState *ps)
} }
#endif #endif
if (ps->blurkernel) {
paint_delete_blur_kernel(ps->blurkernel);
MEM_freeN(ps->blurkernel);
}
if (ps->do_mask_cavity) { if (ps->do_mask_cavity) {
MEM_freeN(ps->cavities); MEM_freeN(ps->cavities);
} }
if (ps->vertFlags) MEM_freeN(ps->vertFlags);
for (a = 0; a < ps->thread_tot; a++) {
BLI_memarena_free(ps->arena_mt[a]);
}
/* copy for subsurf/multires, so throw away */ /* copy for subsurf/multires, so throw away */
if (ps->dm->type != DM_TYPE_CDDM) { if (ps->dm->type != DM_TYPE_CDDM) {
if (ps->dm_mvert) MEM_freeN(ps->dm_mvert); if (ps->dm_mvert) MEM_freeN(ps->dm_mvert);
@ -3987,13 +4056,23 @@ static void project_paint_end(ProjPaintState *ps)
if (ps->dm_release) if (ps->dm_release)
ps->dm->release(ps->dm); ps->dm->release(ps->dm);
}
if (ps->blurkernel) {
paint_delete_blur_kernel(ps->blurkernel);
MEM_freeN(ps->blurkernel);
}
if (ps->vertFlags) MEM_freeN(ps->vertFlags);
for (a = 0; a < ps->thread_tot; a++) {
BLI_memarena_free(ps->arena_mt[a]);
}
} }
/* 1 = an undo, -1 is a redo. */ /* 1 = an undo, -1 is a redo. */
static void partial_redraw_array_init(ImagePaintPartialRedraw *pr) static void partial_redraw_single_init(ImagePaintPartialRedraw *pr)
{ {
int tot = PROJ_BOUNDBOX_SQUARED;
while (tot--) {
pr->x1 = 10000000; pr->x1 = 10000000;
pr->y1 = 10000000; pr->y1 = 10000000;
@ -4001,7 +4080,13 @@ static void partial_redraw_array_init(ImagePaintPartialRedraw *pr)
pr->y2 = -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++; pr++;
} }
} }
@ -4045,6 +4130,8 @@ static bool project_image_refresh_tagged(ProjPaintState *ps)
imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true);
redraw = 1; redraw = 1;
} }
partial_redraw_single_init(pr);
} }
projIma->touch = 0; /* clear for reuse */ projIma->touch = 0; /* clear for reuse */
@ -4884,7 +4971,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po
} }
ups->average_stroke_counter++; ups->average_stroke_counter++;
mul_m4_v3(ps->ob->obmat, world); mul_m4_v3(ps->obmat, world);
add_v3_v3(ups->average_stroke_accum, world); add_v3_v3(ups->average_stroke_accum, world);
ups->last_stroke_valid = true; ups->last_stroke_valid = true;
} }
@ -4894,34 +4981,22 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po
} }
void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], const float pos[2], const bool eraser, float pressure, float distance, float size) 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
)
{ {
ProjPaintState *ps = pps; ProjStrokeHandle *ps_handle = ps_handle_p;
Brush *brush = ps->brush; Brush *brush = ps->brush;
Scene *scene = ps->scene; Scene *scene = ps->scene;
int a;
ps->brush_size = size; ps->brush_size = size;
ps->blend = brush->blend; ps->blend = brush->blend;
if (eraser) if (eraser)
ps->blend = IMB_BLEND_ERASE_ALPHA; ps->blend = IMB_BLEND_ERASE_ALPHA;
/* clone gets special treatment here to avoid going through image initialization */
if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) {
View3D *v3d = ps->v3d;
float *cursor = ED_view3d_cursor3d_get(scene, v3d);
int mval_i[2] = {(int)pos[0], (int)pos[1]};
view3d_operator_needs_opengl(C);
if (!ED_view3d_autodist(scene, ps->ar, v3d, mval_i, cursor, false, NULL))
return;
ED_region_tag_redraw(ps->ar);
return;
}
/* handle gradient and inverted stroke color here */ /* handle gradient and inverted stroke color here */
if (ps->tool == PAINT_TOOL_DRAW) { if (ps->tool == PAINT_TOOL_DRAW) {
paint_brush_color_get(scene, brush, false, ps->mode == BRUSH_STROKE_INVERT, distance, pressure, ps->paint_color, NULL); paint_brush_color_get(scene, brush, false, ps->mode == BRUSH_STROKE_INVERT, distance, pressure, ps->paint_color, NULL);
@ -4941,14 +5016,42 @@ void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], co
} }
} }
/* continue adding to existing partial redraw rects until redraw */ if (project_paint_op(ps, prev_pos, pos)) {
if (!ps->need_redraw) { ps_handle->need_redraw = true;
for (a = 0; a < ps->image_tot; a++) project_image_refresh_tagged(ps);
partial_redraw_array_init(ps->projImages[a].partRedrawRect); }
}
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;
/* clone gets special treatment here to avoid going through image initialization */
if (ps_handle->is_clone_cursor_pick) {
Scene *scene = ps_handle->scene;
View3D *v3d = CTX_wm_view3d(C);
ARegion *ar = CTX_wm_region(C);
float *cursor = ED_view3d_cursor3d_get(scene, v3d);
int mval_i[2] = {(int)pos[0], (int)pos[1]};
view3d_operator_needs_opengl(C);
if (!ED_view3d_autodist(scene, ar, v3d, mval_i, cursor, false, NULL))
return;
ED_region_tag_redraw(ar);
return;
} }
if (project_paint_op(ps, prev_pos, pos)) for (i = 0; i < ps_handle->ps_views_tot; i++) {
ps->need_redraw = true; ProjPaintState *ps = ps_handle->ps_views[i];
paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps);
}
} }
@ -5058,51 +5161,116 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int
void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode) 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"); 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); project_state_init(C, ob, ps, mode);
if (ps->tool == PAINT_TOOL_CLONE && mode == BRUSH_STROKE_INVERT) {
view3d_operator_needs_opengl(C);
return ps;
}
paint_brush_init_tex(ps->brush);
ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW;
if (ps->ob == NULL || !(ps->ob->lay & ps->v3d->lay)) { if (ps->ob == NULL || !(ps->ob->lay & ps->v3d->lay)) {
MEM_freeN(ps); ps_handle->ps_views_tot = i + 1;
return NULL; goto fail;
}
} }
ps->orig_brush_size = BKE_brush_size_get(ps->scene, ps->brush);
/* Don't allow brush size below 2 */ /* Don't allow brush size below 2 */
if (BKE_brush_size_get(ps->scene, ps->brush) < 2) if (BKE_brush_size_get(scene, ps_handle->brush) < 2)
BKE_brush_size_set(ps->scene, ps->brush, 2 * U.pixelsize); BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize);
/* allocate and initialize spatial data structures */ /* allocate and initialize spatial data structures */
project_paint_begin(ps);
if (ps->dm == NULL) { for (i = 0; i < ps_handle->ps_views_tot; i++) {
MEM_freeN(ps); ProjPaintState *ps = ps_handle->ps_views[i];
return NULL;
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(ps, is_multi_view, symmetry_flag_views[i]);
paint_proj_begin_clone(ps, mouse); paint_proj_begin_clone(ps, mouse);
return ps; if (ps->dm == NULL) {
goto fail;
return NULL;
}
}
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;
} }
void paint_proj_redraw(const bContext *C, void *pps, bool final) void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final)
{ {
ProjPaintState *ps = pps; ProjStrokeHandle *ps_handle = ps_handle_p;
if (ps->need_redraw) { if (ps_handle->need_redraw) {
project_image_refresh_tagged(ps); ps_handle->need_redraw = false;
ps->need_redraw = false;
} }
else if (!final) { else if (!final) {
return; return;
@ -5117,19 +5285,34 @@ void paint_proj_redraw(const bContext *C, void *pps, bool final)
} }
} }
void paint_proj_stroke_done(void *pps) void paint_proj_stroke_done(void *ps_handle_p)
{ {
ProjPaintState *ps = pps; ProjStrokeHandle *ps_handle = ps_handle_p;
if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) { Scene *scene = ps_handle->scene;
MEM_freeN(ps); int i;
if (ps_handle->is_clone_cursor_pick) {
MEM_freeN(ps_handle);
return; return;
} }
BKE_brush_size_set(ps->scene, ps->brush, ps->orig_brush_size);
paint_brush_exit_tex(ps->brush); 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);
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); project_paint_end(ps);
MEM_freeN(ps); MEM_freeN(ps);
}
MEM_freeN(ps_handle);
} }
/* use project paint to re-apply an image */ /* use project paint to re-apply an image */
static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
@ -5209,7 +5392,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
ED_image_undo_restore, ED_image_undo_free, NULL); ED_image_undo_restore, ED_image_undo_free, NULL);
/* allocate and initialize spatial data structures */ /* allocate and initialize spatial data structures */
project_paint_begin(&ps); project_paint_begin(&ps, false, 0);
if (ps.dm == NULL) { if (ps.dm == NULL) {
BKE_brush_size_set(scene, ps.brush, orig_brush_size); BKE_brush_size_set(scene, ps.brush, orig_brush_size);

@ -605,6 +605,14 @@ void ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, Object *ob, float pm
mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat); mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat);
} }
void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, float obmat[4][4], float pmat[4][4])
{
float vmat[4][4];
mul_m4_m4m4(vmat, (float (*)[4])rv3d->viewmat, obmat);
mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat);
}
/** /**
* Uses window coordinates (x,y) and depth component z to find a point in * Uses window coordinates (x,y) and depth component z to find a point in
* modelspace */ * modelspace */