* added option "Normal", same as the vpaint option, gives more natural looking brush strokes.

* faces that were ignored were also not taken into acount when checking UV seams - causng bleed not to work properly in some cases.
* commented early pixel filling loop exit that fails in some cases.
This commit is contained in:
Campbell Barton 2008-11-21 03:09:48 +00:00
parent 5ab1256f5b
commit ecfeed042d
3 changed files with 405 additions and 215 deletions

@ -803,10 +803,11 @@ typedef struct Scene {
#define IMAGEPAINT_DRAW_TOOL_DRAWING 4
/* projection painting only */
#define IMAGEPAINT_PROJECT_XRAY 8
#define IMAGEPAINT_PROJECT_BACKFACE 16
#define IMAGEPAINT_PROJECT_XRAY 8
#define IMAGEPAINT_PROJECT_BACKFACE 16
#define IMAGEPAINT_PROJECT_IGNORE_SEAMS 32
#define IMAGEPAINT_PROJECT_CLONE_LAYER 64
#define IMAGEPAINT_PROJECT_FLAT 128
/* toolsettings->uvcalc_flag */
#define UVCALC_FILLHOLES 1

@ -6373,7 +6373,9 @@ static void editing_panel_mesh_paint(void)
/* Projection Painting */
uiBlockBeginAlign(block);
uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_XRAY, B_NOP, "Occlude", xco+10,yco-70,butw,19, &settings->imapaint.flag, 0, 0, 0, 0, "Only paint onto the faces directly under the brush (slower)");
uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_BACKFACE, B_NOP, "Backface Cull", xco+10,yco-90,butw,19, &settings->imapaint.flag, 0, 0, 0, 0, "Ignore faces pointing away from the view (faster)");
uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_BACKFACE, B_NOP, "Cull", xco+10,yco-90,butw/2,19, &settings->imapaint.flag, 0, 0, 0, 0, "Ignore faces pointing away from the view (faster)");
uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_FLAT, B_NOP, "Normal", xco+10+butw/2,yco-90,butw/2,19, &settings->imapaint.flag, 0, 0, 0, 0, "Ignore faces pointing away from the view (faster)");
uiDefButBitS(block, TOGN|BIT, IMAGEPAINT_PROJECT_IGNORE_SEAMS, B_NOP, "Bleed", xco+10,yco-110,butw/2,19, &settings->imapaint.flag, 0, 0, 0, 0, "Extend paint beyond the faces UVs to reduce seams (in pixels, slower)");
uiDefButF(block, NUM, B_NOP, "", xco+10 + (butw/2),yco-110,butw/2,19, &settings->imapaint.seam_bleed, 2.0, 8.0, 0, 0, "Extend paint beyond the faces UVs to reduce seams (in pixels, slower)");
uiBlockEndAlign(block);

@ -178,6 +178,9 @@ typedef struct ImagePaintPartialRedraw {
#define PROJ_BUCKET_INIT 1<<0
// #define PROJ_BUCKET_CLONE_INIT 1<<1
/* vert flags */
#define PROJ_VERT_CULL 1
/* only for readability */
#define PROJ_BUCKET_LEFT 0
#define PROJ_BUCKET_RIGHT 1
@ -221,6 +224,7 @@ typedef struct ProjPaintState {
char *faceSeamFlags; /* store info about faces, if they are initialized etc*/
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 */
char *vertFlags; /* store options per vert, now only store if the vert is pointing away from the view */
#endif
int buckets_x; /* The size of the bucket grid, the grid span's screen_min/screen_max so you can paint outsize the screen or with 2 brushes at once */
int buckets_y;
@ -235,6 +239,7 @@ typedef struct ProjPaintState {
short do_occlude; /* Use raytraced occlusion? - ortherwise will paint right through to the back*/
short do_backfacecull; /* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */
short is_ortho;
short do_mask_normal; /* mask out pixels based on their normals */
#ifndef PROJ_DEBUG_NOSEAMBLEED
float seam_bleed_px;
#endif
@ -245,6 +250,7 @@ typedef struct ProjPaintState {
float projectMat[4][4]; /* Projection matrix, use for getting screen coords */
float viewMat[4][4];
float viewDir[3]; /* View vector, use for do_backfacecull and for ray casting with an ortho viewport */
float viewPos[3]; /* View location in object relative 3D space, so can compare to verts */
float screen_min[2]; /* 2D bounds for mesh verts on the screen's plane (screenspace) */
float screen_max[2];
@ -278,8 +284,10 @@ typedef struct ProjPixel {
PixelStore origColor;
PixelPointer pixel;
short image_index; /* if anyone wants to paint onto more then 32000 images they can bite me */
short bb_cell_index;
float mask; /* for various reasons we may want to mask out painting onto this pixel */
short image_index; /* if anyone wants to paint onto more then 32768 images they can bite me */
unsigned char bb_cell_index;
} ProjPixel;
typedef struct ProjPixelClone {
@ -583,6 +591,19 @@ static void BarycentricWeightsSimplePersp2f(float v1[4], float v2[4], float v3[4
}
}
static VecWeightf(float p[3], float v1[3], float v2[3], float v3[3], float w[3])
{
p[0] = v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2];
p[1] = v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2];
p[2] = v1[2]*w[0] + v2[2]*w[1] + v3[2]*w[2];
}
static Vec2Weightf(float p[3], float v1[3], float v2[3], float v3[3], float w[3])
{
p[0] = v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2];
p[1] = v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2];
}
static float tri_depth_2d(float v1[3], float v2[3], float v3[3], float pt[2], float w[3])
{
BarycentricWeightsSimple2f(v1,v2,v3,pt,w);
@ -666,12 +687,10 @@ static int project_paint_PickColor(ProjPaintState *ps, float pt[2], float *rgba_
tf = ps->dm_mtface + face_index;
if (side==0) {
uv[0] = tf->uv[0][0]*w[0] + tf->uv[1][0]*w[1] + tf->uv[2][0]*w[2];
uv[1] = tf->uv[0][1]*w[0] + tf->uv[1][1]*w[1] + tf->uv[2][1]*w[2];
if (side == 0) {
Vec2Weightf(uv, tf->uv[0], tf->uv[1], tf->uv[2], w);
} else { /* QUAD */
uv[0] = tf->uv[0][0]*w[0] + tf->uv[2][0]*w[1] + tf->uv[3][0]*w[2];
uv[1] = tf->uv[0][1]*w[0] + tf->uv[2][1]*w[1] + tf->uv[3][1]*w[2];
Vec2Weightf(uv, tf->uv[0], tf->uv[2], tf->uv[3], w);
}
ibuf = BKE_image_get_ibuf((Image *)tf->tpage, NULL); /* TODO - this may be slow */
@ -774,9 +793,8 @@ static int project_paint_PointOcclude(float pt[3], float v1[3], float v2[3], flo
/* 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 dosn't need to be correct in relation to X and Y coords (this is the case in perspective view) */
static int project_bucket_point_occluded(ProjPaintState *ps, int bucket_index, int orig_face, float pixelScreenCo[4])
static int project_bucket_point_occluded(ProjPaintState *ps, LinkNode *bucketFace, int orig_face, float pixelScreenCo[4])
{
LinkNode *node = ps->bucketFaces[bucket_index];
MFace *mf;
int face_index;
int isect_ret;
@ -784,8 +802,8 @@ static int project_bucket_point_occluded(ProjPaintState *ps, int bucket_index, i
/* 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 */
while (node) {
face_index = (int)node->link;
while (bucketFace) {
face_index = (int)bucketFace->link;
if (orig_face != face_index) {
@ -812,7 +830,7 @@ static int project_bucket_point_occluded(ProjPaintState *ps, int bucket_index, i
return 1;
}
}
node = node->next;
bucketFace = bucketFace->next;
}
return 0;
@ -1151,20 +1169,19 @@ static void screen_px_from_ortho(
float w[3])
{
BarycentricWeightsSimple2f(uv1co,uv2co,uv3co,uv,w);
pixelScreenCo[0] = v1co[0]*w[0] + v2co[0]*w[1] + v3co[0]*w[2];
pixelScreenCo[1] = v1co[1]*w[0] + v2co[1]*w[1] + v3co[1]*w[2];
pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2];
VecWeightf(pixelScreenCo, v1co, v2co, v3co, w);
}
/* same as screen_px_from_ortho except we need to take into account
* the perspective W coord for each vert */
static void screen_px_from_persp(
ProjPaintState *ps, float uv[2],
float v1co[3], float v2co[3], float v3co[3], /* Worldspace coords */
float v1co[3], float v2co[3], float v3co[3], /* screenspace coords */
float uv1co[2], float uv2co[2], float uv3co[2],
float pixelScreenCo[4],
float w[3])
{
float wtot_inv, wtot;
BarycentricWeightsSimple2f(uv1co,uv2co,uv3co,uv,w);
@ -1185,149 +1202,230 @@ static void screen_px_from_persp(
}
/* done re-weighting */
VecWeightf(pixelScreenCo, v1co, v2co, v3co, w);
}
#if 0
static void screen_px_from_persp(
ProjPaintState *ps, float uv[2],
float v1co[3], float v2co[3], float v3co[3], /* Worldspace coords */
float uv1co[2], float uv2co[2], float uv3co[2],
float pixelScreenCo[4],
float w[3])
{
BarycentricWeightsSimple2f(uv1co,uv2co,uv3co,uv,w);
pixelScreenCo[0] = v1co[0]*w[0] + v2co[0]*w[1] + v3co[0]*w[2];
pixelScreenCo[1] = v1co[1]*w[0] + v2co[1]*w[1] + v3co[1]*w[2];
pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2];
pixelScreenCo[2] = v1co[2]*w[0] + v2co[2]*w[1] + v3co[2]*w[2];
pixelScreenCo[3] = 1.0;
Mat4MulVec4fl(ps->projectMat, pixelScreenCo);
// if( pixelScreenCo[3] > 0.001 ) { ??? TODO
/* screen space, not clamped */
pixelScreenCo[0] = (float)(curarea->winx/2.0)+(curarea->winx/2.0)*pixelScreenCo[0]/pixelScreenCo[3];
pixelScreenCo[1] = (float)(curarea->winy/2.0)+(curarea->winy/2.0)*pixelScreenCo[1]/pixelScreenCo[3];
pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */
}
#endif
/* Only run this function once for new ProjPixelClone's */
#define IMA_CHAR_PX_SIZE 4
/* run this outside project_paint_uvpixel_init since pixels with mask 0 dont need init */
float project_paint_uvpixel_mask(
ProjPaintState *ps,
int face_index,
int side,
float w[3]
) {
float mask;
/* calculate mask */
if (ps->do_mask_normal) {
MFace *mf = ps->dm_mface + face_index;
short *no1, *no2, *no3;
float no[3], angle;
no1 = ps->dm_mvert[mf->v1].no;
if (side==1) {
no2 = ps->dm_mvert[mf->v3].no;
no3 = ps->dm_mvert[mf->v4].no;
} else {
no2 = ps->dm_mvert[mf->v2].no;
no3 = ps->dm_mvert[mf->v3].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(no);
/* now we can use the normal as a mask */
if (ps->is_ortho) {
angle = NormalizedVecAngle2(ps->viewDir, no);
} else {
/* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */
float viewDirPersp[3];
float *co1, *co2, *co3;
co1 = ps->dm_mvert[mf->v1].co;
if (side==1) {
co2 = ps->dm_mvert[mf->v3].co;
co3 = ps->dm_mvert[mf->v4].co;
} else {
co2 = ps->dm_mvert[mf->v2].co;
co3 = ps->dm_mvert[mf->v3].co;
}
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(viewDirPersp);
angle = NormalizedVecAngle2(viewDirPersp, no);
}
if (angle >= M_PI_2) {
mask = 0.0f;
} else {
angle = 1.0f - (angle / M_PI_2); /* map angle to 1.0-facing us, 0.0 right angles to the view direction */
mask = ((angle*angle) + angle) * 0.5f; /* nice mix of normal value and power of 2 */
}
} else {
mask = 1.0f;
}
return mask;
}
/* run this function when we know a bucket's, face's pixel can be initialized,
* adding to the LinkList 'ps->bucketRect' */
static void project_paint_uvpixel_init(ProjPaintState *ps, int thread_index, ImBuf *ibuf, short x, short y, int bucket_index, int face_index, int image_index, float pixelScreenCo[4], int side, float w[3])
{
* return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */
static ProjPixel *project_paint_uvpixel_init(
ProjPaintState *ps,
MemArena *arena,
ImBuf *ibuf,
short x, short y,
float mask,
int face_index,
int image_index,
float pixelScreenCo[4],
int side,
float w[3]
) {
ProjPixel *projPixel;
short size;
// printf("adding px (%d %d), (%f %f)\n", x,y,uv[0],uv[1]);
/* wrap pixel location */
x = x % ibuf->x;
if (x<0) x += ibuf->x;
y = y % ibuf->y;
if (y<0) y += ibuf->y;
/* Use screen_min to make (0,0) the bottom left of the bounds
* Then this can be used to index the bucket array */
/* 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) == face_index) {
if (ps->do_occlude==0 || !project_bucket_point_occluded(ps, bucket_index, face_index, pixelScreenCo)) {
/* wrap pixel location */
// printf(" %d %d", x, y);
x = x % ibuf->x;
if (x<0) x += ibuf->x;
y = y % ibuf->y;
if (y<0) y += ibuf->y;
// printf(" %d %d\n", x, y);
if (ps->tool==PAINT_TOOL_CLONE) {
if (ibuf->rect_float) {
size = sizeof(ProjPixelCloneFloat);
} else {
size = sizeof(ProjPixelClone);
}
} else if (ps->tool==PAINT_TOOL_SMEAR) {
size = sizeof(ProjPixelClone);
} else {
size = sizeof(ProjPixel);
}
projPixel = (ProjPixel *)BLI_memarena_alloc(ps->arena_mt[thread_index], size);
if (ps->tool==PAINT_TOOL_CLONE) {
if (ibuf->rect_float) {
projPixel->pixel.f_pt = ((( float * ) ibuf->rect_float) + (( x + y * ibuf->x ) * IMA_CHAR_PX_SIZE));
/* TODO float support for origColor */
size = sizeof(ProjPixelCloneFloat);
} else {
projPixel->pixel.ch_pt = ((( unsigned char * ) ibuf->rect) + (( x + y * ibuf->x ) * IMA_CHAR_PX_SIZE));
projPixel->origColor.uint = *projPixel->pixel.uint_pt;
size = sizeof(ProjPixelClone);
}
/* screenspace unclamped, we could keep its z and w values but dont need them at the moment */
VECCOPY2D(projPixel->projCo2D, pixelScreenCo);
projPixel->x_px = x;
projPixel->y_px = y;
/* which bounding box cell are we in? */
projPixel->bb_cell_index = ((int)((((float)x)/((float)ibuf->x)) * PROJ_BOUNDBOX_DIV)) + ((int)((((float)y)/((float)ibuf->y)) * PROJ_BOUNDBOX_DIV)) * PROJ_BOUNDBOX_DIV ;
/* done with view3d_project_float inline */
if (ps->tool==PAINT_TOOL_CLONE) {
if (ps->dm_mtface_clone) {
/* TODO - float buffer */
ImBuf *ibuf_other;
MTFace *tf_other = ps->dm_mtface_clone + face_index;
float *uvCo1, *uvCo2, *uvCo3;
if (side==1) {
uvCo1 = tf_other->uv[0];
uvCo2 = tf_other->uv[2];
uvCo3 = tf_other->uv[3];
} else {
uvCo1 = tf_other->uv[0];
uvCo2 = tf_other->uv[1];
uvCo3 = tf_other->uv[2];
}
((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
if (tf_other->tpage && ( ibuf_other = BKE_image_get_ibuf((Image *)tf_other->tpage, NULL) )) {
/* BKE_image_get_ibuf - TODO - this may be slow */
float uv_other[2], x, y;
uv_other[0] = w[0]*uvCo1[0] + w[1]*uvCo2[0] + w[2]*uvCo3[0];
uv_other[1] = w[0]*uvCo1[1] + w[1]*uvCo2[1] + w[2]*uvCo3[1];
/* use */
x = (float)fmod(uv_other[0], 1.0f);
y = (float)fmod(uv_other[1], 1.0f);
if (x < 0.0f) x += 1.0f;
if (y < 0.0f) y += 1.0f;
x = x * ibuf_other->x - 0.5f;
y = y * ibuf_other->y - 0.5f;
/* TODO - float buffer check */
bilinear_interpolation_color(ibuf_other, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL, x, y);
} else {
((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
}
} else if (ps->tool==PAINT_TOOL_SMEAR) {
size = sizeof(ProjPixelClone);
} else {
size = sizeof(ProjPixel);
}
projPixel = (ProjPixel *)BLI_memarena_alloc(arena, size);
if (ibuf->rect_float) {
projPixel->pixel.f_pt = ((( float * ) ibuf->rect_float) + (( x + y * ibuf->x ) * IMA_CHAR_PX_SIZE));
/* TODO float support for origColor */
} else {
projPixel->pixel.ch_pt = ((( unsigned char * ) ibuf->rect) + (( x + y * ibuf->x ) * IMA_CHAR_PX_SIZE));
projPixel->origColor.uint = *projPixel->pixel.uint_pt;
}
/* screenspace unclamped, we could keep its z and w values but dont need them at the moment */
VECCOPY2D(projPixel->projCo2D, pixelScreenCo);
projPixel->x_px = x;
projPixel->y_px = y;
projPixel->mask = mask;
/* which bounding box cell are we in?, needed for undo */
projPixel->bb_cell_index = ((int)((((float)x)/((float)ibuf->x)) * PROJ_BOUNDBOX_DIV)) + ((int)((((float)y)/((float)ibuf->y)) * PROJ_BOUNDBOX_DIV)) * PROJ_BOUNDBOX_DIV ;
/* done with view3d_project_float inline */
if (ps->tool==PAINT_TOOL_CLONE) {
if (ps->dm_mtface_clone) {
/* TODO - float buffer */
ImBuf *ibuf_other;
MTFace *tf_other = ps->dm_mtface_clone + face_index;
float *uvCo1, *uvCo2, *uvCo3;
uvCo1 = tf_other->uv[0];
if (side==1) {
uvCo2 = tf_other->uv[2];
uvCo3 = tf_other->uv[3];
} else {
float co[2];
uvCo2 = tf_other->uv[1];
uvCo3 = tf_other->uv[2];
}
((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
if (tf_other->tpage && ( ibuf_other = BKE_image_get_ibuf((Image *)tf_other->tpage, NULL) )) {
/* BKE_image_get_ibuf - TODO - this may be slow */
float uv_other[2], x, y;
/* Initialize clone pixels - note that this is a bit of a waste since some of these are being indirectly initialized :/ */
/* TODO - possibly only run this for directly ativated buckets when cloning */
Vec2Subf(co, projPixel->projCo2D, ps->clone_offset);
Vec2Weightf(uv_other, uvCo1, uvCo2, uvCo3, w);
/* no need to initialize the bucket, we're only checking buckets faces and for this
* the faces are alredy initialized in project_paint_delayed_face_init(...) */
if (ibuf->rect_float) {
if (!project_paint_PickColor(ps, co, ((ProjPixelCloneFloat *)projPixel)->clonepx_f, NULL, 1)) {
((ProjPixelCloneFloat *)projPixel)->clonepx_f[3] = 0; /* zero alpha - ignore */
}
} else {
if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) {
((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; /* zero alpha - ignore */
}
/* use */
x = (float)fmod(uv_other[0], 1.0f);
y = (float)fmod(uv_other[1], 1.0f);
if (x < 0.0f) x += 1.0f;
if (y < 0.0f) y += 1.0f;
x = x * ibuf_other->x - 0.5f;
y = y * ibuf_other->y - 0.5f;
/* TODO - float buffer check */
bilinear_interpolation_color(ibuf_other, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL, x, y);
} else {
((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0;
}
} else {
float co[2];
/* Initialize clone pixels - note that this is a bit of a waste since some of these are being indirectly initialized :/ */
/* TODO - possibly only run this for directly ativated buckets when cloning */
Vec2Subf(co, projPixel->projCo2D, ps->clone_offset);
/* no need to initialize the bucket, we're only checking buckets faces and for this
* the faces are alredy initialized in project_paint_delayed_face_init(...) */
if (ibuf->rect_float) {
if (!project_paint_PickColor(ps, co, ((ProjPixelCloneFloat *)projPixel)->clonepx_f, NULL, 1)) {
((ProjPixelCloneFloat *)projPixel)->clonepx_f[3] = 0; /* zero alpha - ignore */
}
} else {
if (!project_paint_PickColor(ps, co, NULL, ((ProjPixelClone *)projPixel)->clonepx.ch, 1)) {
((ProjPixelClone *)projPixel)->clonepx.ch[3] = 0; /* zero alpha - ignore */
}
}
}
#ifdef PROJ_DEBUG_PAINT
if (ibuf->rect_float) ((float *)projPixel->pixel)[1] = 0;
else ((char *)projPixel->pixel)[1] = 0;
#endif
projPixel->image_index = image_index;
BLI_linklist_prepend_arena(
&ps->bucketRect[ bucket_index ],
projPixel,
ps->arena_mt[thread_index]
);
}
#ifdef PROJ_DEBUG_PAINT
if (ibuf->rect_float) ((float *)projPixel->pixel)[1] = 0;
else ((char *)projPixel->pixel)[1] = 0;
#endif
projPixel->image_index = image_index;
return projPixel;
}
/* intersect two 2D boundboxes */
@ -1482,32 +1580,27 @@ static void rect_to_uvspace(
/* get the UV space bounding box */
uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT];
uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM];
if (ps->is_ortho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
bucket_bounds_uv[0][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2];
bucket_bounds_uv[0][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2];
if (ps->is_ortho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
Vec2Weightf(bucket_bounds_uv[0], uv1co, uv2co, uv3co, w);
//uv[0] = bucket_bounds[PROJ_BUCKET_RIGHT]; // set above
uv[1] = bucket_bounds[PROJ_BUCKET_TOP];
if (ps->is_ortho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
bucket_bounds_uv[1][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2];
bucket_bounds_uv[1][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2];
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
Vec2Weightf(bucket_bounds_uv[1], uv1co, uv2co, uv3co, w);
uv[0] = bucket_bounds[PROJ_BUCKET_LEFT];
//uv[1] = bucket_bounds[PROJ_BUCKET_TOP]; // set above
if (ps->is_ortho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
bucket_bounds_uv[2][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2];
bucket_bounds_uv[2][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2];
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
Vec2Weightf(bucket_bounds_uv[2], uv1co, uv2co, uv3co, w);
//uv[0] = bucket_bounds[PROJ_BUCKET_LEFT]; // set above
uv[1] = bucket_bounds[PROJ_BUCKET_BOTTOM];
if (ps->is_ortho) BarycentricWeights2f(v1coSS, v2coSS, v3coSS, uv, w);
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
bucket_bounds_uv[3][0] = uv1co[0]*w[0] + uv2co[0]*w[1] + uv3co[0]*w[2];
bucket_bounds_uv[3][1] = uv1co[1]*w[0] + uv2co[1]*w[1] + uv3co[1]*w[2];
else BarycentricWeightsPersp2f(v1coSS, v2coSS, v3coSS, uv, w);
Vec2Weightf(bucket_bounds_uv[3], uv1co, uv2co, uv3co, w);
}
@ -1516,6 +1609,9 @@ static void rect_to_uvspace(
static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bucket_index, int face_index, int image_index, float bucket_bounds[4], ImBuf *ibuf)
{
/* 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];
MFace *mf = ps->dm_mface + face_index;
MTFace *tf = ps->dm_mtface + face_index;
@ -1523,11 +1619,14 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
/* UV/pixel seeking data */
int x; /* Image X-Pixel */
int y;/* Image Y-Pixel */
float mask;
float uv[2]; /* Image floating point UV - same as x,y but from 0.0-1.0 */
int min_px[2], max_px[2]; /* UV Bounds converted to int's for pixel */
int min_px_tf[2], max_px_tf[2]; /* UV Bounds converted to int's for pixel */
int min_px_bucket[2], max_px_bucket[2]; /* Bucket Bounds converted to int's for pixel */
int side;
float *v1coSS, *v2coSS, *v3coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */
float *vCo[4]; /* vertex screenspace coords */
@ -1536,7 +1635,6 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
float *uv1co, *uv2co, *uv3co; /* for convenience only, these will be assigned to tf->uv[0],1,2 or tf->uv[0],2,3 */
float pixelScreenCo[4];
int i;
/* vars for getting uvspace bounds */
float bucket_bounds_uv[4][2]; /* bucket bounds in UV space so we can init pixels only for this face, */
@ -1545,7 +1643,7 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
float xhalfpx, yhalfpx;
float ibuf_xf = ibuf->x, ibuf_yf = ibuf->y;
int has_x_isect = -1, has_isect = -1; /* for early loop exit */
int has_x_isect = 0, has_isect = 0; /* for early loop exit */
int i1,i2,i3;
@ -1575,13 +1673,13 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
tf_uv_pxoffset[3][0] = tf->uv[3][0] - xhalfpx;
tf_uv_pxoffset[3][1] = tf->uv[3][1] - yhalfpx;
i = 1;
side = 1;
} else {
i = 0;
side = 0;
}
do {
if (i==1) {
if (side==1) {
i1=0; i2=2; i3=3;
} else {
i1=0; i2=1; i3=2;
@ -1625,9 +1723,26 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
screen_px_from_ortho(ps, uv, v1coSS,v2coSS,v3coSS, uv1co,uv2co,uv3co, pixelScreenCo, w);
} else {
screen_px_from_persp(ps, uv, v1coSS,v2coSS,v3coSS, uv1co,uv2co,uv3co, pixelScreenCo, w);
//screen_px_from_persp(ps, uv, vCo[i1],vCo[i2],vCo[i3], uv1co,uv2co,uv3co, pixelScreenCo, w);
}
project_paint_uvpixel_init(ps, thread_index, ibuf, x, y, bucket_index, face_index, image_index, pixelScreenCo, i, w);
/* 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) == face_index) {
if (ps->do_occlude==0 || !project_bucket_point_occluded(ps, bucketFaceNodes, face_index, pixelScreenCo)) {
mask = project_paint_uvpixel_mask(ps, face_index, side, w);
if (mask > 0.0f) {
BLI_linklist_prepend_arena(
bucketPixelNodes,
project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, side, w),
arena
);
}
}
has_x_isect = has_isect = 1;
} else if (has_x_isect) {
@ -1636,13 +1751,17 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
}
}
#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;
break;
}
#endif
}
}
} while(i--);
} while(side--);
#ifndef PROJ_DEBUG_NOSEAMBLEED
if (ps->seam_bleed_px > 0.0f) {
int face_seam_flag;
@ -1682,7 +1801,6 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
float bucket_clip_edges[2][2]; /* store the screenspace coords of the face, clipped by the bucket's screen aligned rectangle */
float edge_verts_inset_clip[2][3];
int fidx1, fidx2; /* 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 side;
float seam_subsection[4][2];
float fac1, fac2, ftot;
@ -1703,11 +1821,11 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
vCoSS[3] = ps->screenCoords[ mf->v4 ];
if (ps->is_ortho) {
if (mf->v4) scale_quad(vCoSS, insetCos, 0.99999);
else scale_tri(vCoSS, insetCos, 0.99999);
if (mf->v4) scale_quad(vCoSS, insetCos, 0.99999f);
else scale_tri(vCoSS, insetCos, 0.99999f);
} else {
if (mf->v4) scale_quad(vCo, insetCos, 0.99999);
else scale_tri(vCo, insetCos, 0.99999);
if (mf->v4) scale_quad(vCo, insetCos, 0.99999f);
else scale_tri(vCo, insetCos, 0.99999f);
}
for (fidx1 = 0; fidx1 < (mf->v4 ? 4 : 3); fidx1++) {
@ -1786,28 +1904,42 @@ static void project_paint_face_init(ProjPaintState *ps, int thread_index, int bu
pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */
}
if (ps->dm_mtface_clone) {
/* TODO, this is not QUITE correct since UV is not inside the UV's but good enough for seams */
if (side) {
BarycentricWeightsSimple2f(tf_uv_pxoffset[0], tf_uv_pxoffset[2], tf_uv_pxoffset[3], uv, w);
} else {
BarycentricWeightsSimple2f(tf_uv_pxoffset[0], tf_uv_pxoffset[1], tf_uv_pxoffset[2], uv, w);
if (ps->do_occlude==0 || !project_bucket_point_occluded(ps, bucketFaceNodes, face_index, pixelScreenCo)) {
/* Only bother calculating the weights if we intersect */
if (ps->do_mask_normal || ps->dm_mtface_clone) {
/* TODO, this is not QUITE correct since UV is not inside the UV's but good enough for seams */
if (side) {
BarycentricWeightsSimple2f(tf_uv_pxoffset[0], tf_uv_pxoffset[2], tf_uv_pxoffset[3], uv, w);
} else {
BarycentricWeightsSimple2f(tf_uv_pxoffset[0], tf_uv_pxoffset[1], tf_uv_pxoffset[2], uv, w);
}
}
mask = project_paint_uvpixel_mask(ps, face_index, side, w);
if (mask > 0.0f) {
BLI_linklist_prepend_arena(
bucketPixelNodes,
project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, side, w),
arena
);
}
}
project_paint_uvpixel_init(ps, thread_index, ibuf, x, y, bucket_index, face_index, image_index, pixelScreenCo, side, w); /* TODO - side */
} 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
}
}
}
@ -1985,17 +2117,6 @@ static void project_paint_delayed_face_init(ProjPaintState *ps, MFace *mf, MTFac
a = (*(&mf->v1 + i)); /* vertex index */
DO_MINMAX2(ps->screenCoords[ a ], min, max);
#ifndef PROJ_DEBUG_NOSEAMBLEED
/* add face user if we have bleed enabled, set the UV seam flags later */
if (ps->seam_bleed_px > 0.0f) {
BLI_linklist_prepend_arena(
&ps->vertFaces[ a ],
(void *)face_index, /* cast to a pointer to shut up the compiler */
ps->arena
);
}
#endif
} while (i--);
project_paint_rect(ps, min, max, bucket_min, bucket_max);
@ -2040,10 +2161,10 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
{
/* Viewport vars */
float mat[3][3];
float f_no[3];
float viewPos[3]; /* for projection only - view location */
float clipsta;
float no[3];
float (*projScreenCo)[4]; /* Note, we could have 4D vectors are only needed for */
float projMargin;
/* Image Vars - keep track of images we have used */
@ -2059,7 +2180,7 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
MTFace *tf;
int a, i; /* generic looping vars */
int image_index;
int image_index, face_index;
/* memory sized to add to arena size */
int tot_bucketRectMem=0;
@ -2068,6 +2189,7 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
int tot_bucketFacesMem=0;
int tot_bucketFlagsMem=0;
int tot_vertFacesMem=0;
int tot_vertFlagsMem=0;
/* ---- end defines ---- */
@ -2136,10 +2258,10 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
Mat4MulVec4fl(ps->projectMat, (*projScreenCo));
if( (*projScreenCo)[3] > clipsta ) {
if ( (*projScreenCo)[3] > clipsta ) {
/* screen space, not clamped */
(*projScreenCo)[0] = (float)(curarea->winx/2.0f)+(curarea->winx/2.0)*(*projScreenCo)[0]/(*projScreenCo)[3];
(*projScreenCo)[1] = (float)(curarea->winy/2.0f)+(curarea->winy/2.0)*(*projScreenCo)[1]/(*projScreenCo)[3];
(*projScreenCo)[0] = (float)(curarea->winx/2.0f)+(curarea->winx/2.0f)*(*projScreenCo)[0]/(*projScreenCo)[3];
(*projScreenCo)[1] = (float)(curarea->winy/2.0f)+(curarea->winy/2.0f)*(*projScreenCo)[1]/(*projScreenCo)[3];
(*projScreenCo)[2] = (*projScreenCo)[2]/(*projScreenCo)[3]; /* Use the depth for bucket point occlusion */
DO_MINMAX2((*projScreenCo), ps->screen_min, ps->screen_max);
} else {
@ -2193,13 +2315,18 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
}
#endif
if (ps->do_backfacecull && ps->do_mask_normal) {
tot_vertFlagsMem = sizeof(char) * ps->dm_totvert;
}
/* BLI_memarena_new uses calloc */
ps->arena =
BLI_memarena_new( tot_bucketRectMem +
tot_bucketFacesMem +
tot_faceSeamFlagsMem +
tot_faceSeamUVMem +
tot_vertFacesMem + (1<<18));
tot_vertFacesMem +
tot_vertFlagsMem + (1<<18));
BLI_memarena_use_calloc(ps->arena);
@ -2208,7 +2335,7 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
ps->bucketFlags= (char *)BLI_memarena_alloc( ps->arena, tot_bucketFlagsMem);
#ifndef PROJ_DEBUG_NOSEAMBLEED
if (ps->seam_bleed_px > 0.0) {
if (ps->seam_bleed_px > 0.0f) {
ps->vertFaces= (LinkNode **)BLI_memarena_alloc( ps->arena, tot_vertFacesMem);
ps->faceSeamFlags = (char *)BLI_memarena_alloc( ps->arena, tot_faceSeamFlagsMem);
ps->faceSeamUVs= BLI_memarena_alloc( ps->arena, tot_faceSeamUVMem);
@ -2221,7 +2348,7 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
#ifndef PROJ_DEBUG_NOSEAMBLEED
// calloced - memset(ps->faceSeamFlags,0, tot_faceSeamFlagsMem);
// calloced - if (ps->seam_bleed_px > 0.0) {
// calloced - if (ps->seam_bleed_px > 0.0f) {
// calloced - memset(ps->vertFaces, 0, tot_vertFacesMem);
/* TODO dosnt need zeroing? */
// calloced - memset(ps->faceSeamUVs, 0, tot_faceSeamUVMem);
@ -2249,7 +2376,32 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
Mat3MulVecfl(mat, ps->viewDir);
Mat3CpyMat4(mat, ps->ob->imat);
Mat3MulVecfl(mat, ps->viewDir);
Normalize(ps->viewDir);
if (ps->do_backfacecull && ps->do_mask_normal) {
MVert *v = ps->dm_mvert;
float viewDirPersp[3];
ps->vertFlags = BLI_memarena_alloc( ps->arena, tot_vertFlagsMem);
for(a=0; a < ps->dm_totvert; a++, v++) {
no[0] = (float)(v->no[0] / 32767.0f);
no[1] = (float)(v->no[1] / 32767.0f);
no[2] = (float)(v->no[2] / 32767.0f);
if (ps->is_ortho) {
if (NormalizedVecAngle2(ps->viewDir, no) >= M_PI_2) { /* 1 vert of this face is towards us */
ps->vertFlags[a] |= PROJ_VERT_CULL;
}
} else {
VecSubf(viewDirPersp, ps->viewPos, v->co);
if (NormalizedVecAngle2(ps->viewDir, no) >= M_PI_2) { /* 1 vert of this face is towards us */
ps->vertFlags[a] |= PROJ_VERT_CULL;
}
}
}
}
/* setup clone offset */
if (ps->tool == PAINT_TOOL_CLONE) {
float projCo[4];
@ -2269,13 +2421,27 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
if (!ps->is_ortho) {
/* get the view direction relative to the objects matrix */
float imat[3][3];
VECCOPY(viewPos, G.vd->viewinv[3]);
VECCOPY(ps->viewPos, G.vd->viewinv[3]);
Mat3CpyMat4(imat, ps->ob->imat);
Mat3MulVecfl(imat, viewPos);
VecAddf(viewPos, viewPos, ps->ob->imat[3]);
Mat3MulVecfl(imat, ps->viewPos);
VecAddf(ps->viewPos, ps->viewPos, ps->ob->imat[3]);
}
for( a = 0, tf = ps->dm_mtface, mf = ps->dm_mface; a < ps->dm_totface; mf++, tf++, a++ ) {
for( face_index = 0, tf = ps->dm_mtface, mf = ps->dm_mface; face_index < ps->dm_totface; mf++, tf++, face_index++ ) {
#ifndef PROJ_DEBUG_NOSEAMBLEED
/* 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) {
BLI_linklist_prepend_arena( &ps->vertFaces[ mf->v1 ], (void *)face_index, ps->arena);
BLI_linklist_prepend_arena( &ps->vertFaces[ mf->v2 ], (void *)face_index, ps->arena);
BLI_linklist_prepend_arena( &ps->vertFaces[ mf->v3 ], (void *)face_index, ps->arena);
if (mf->v4) {
BLI_linklist_prepend_arena( &ps->vertFaces[ mf->v4 ], (void *)face_index, ps->arena);
}
}
#endif
if (tf->tpage && ((G.f & G_FACESELECT)==0 || mf->flag & ME_FACE_SEL)) {
float *v1coSS, *v2coSS, *v3coSS, *v4coSS;
@ -2325,14 +2491,32 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
}
#endif //PROJ_DEBUG_WINCLIP
if (ps->do_backfacecull) {
/* TODO - we dont really need the normal, just the direction, save a sqrt? */
if (mf->v4) CalcNormFloat4(v1coSS, v2coSS, v3coSS, v4coSS, f_no);
else CalcNormFloat(v1coSS, v2coSS, v3coSS, f_no);
if (f_no[2] < 0.0f) {
continue;
if (ps->do_mask_normal) {
/* Since we are interpolating the normals of faces, we want to make
* sure all the verts are pointing away from the view,
* not just the face */
if ( (ps->vertFlags[mf->v1] & PROJ_VERT_CULL) &&
(ps->vertFlags[mf->v2] & PROJ_VERT_CULL) &&
(ps->vertFlags[mf->v3] & PROJ_VERT_CULL) &&
(mf->v4==0 || ps->vertFlags[mf->v4] & PROJ_VERT_CULL)
) {
continue;
}
} else {
/* TODO - we dont really need the normal, just the direction, save a sqrt? */
if (mf->v4) CalcNormFloat4(v1coSS, v2coSS, v3coSS, v4coSS, no);
else CalcNormFloat(v1coSS, v2coSS, v3coSS, no);
if (no[2] < 0.0f) {
continue;
}
}
}
@ -2355,7 +2539,7 @@ static void project_paint_begin( ProjPaintState *ps, short mval[2])
if (ibuf) {
/* Initialize the faces screen pixels */
/* Add this to a list to initialize later */
project_paint_delayed_face_init(ps, mf, tf, a);
project_paint_delayed_face_init(ps, mf, tf, face_index);
}
}
}
@ -3199,7 +3383,7 @@ static void *do_projectpaint_thread(void *ph_v)
case PAINT_TOOL_CLONE:
if (is_floatbuf) {
if (((ProjPixelCloneFloat*)projPixel)->clonepx_f[3]) {
alpha = brush_sample_falloff(ps->brush, dist);
alpha = brush_sample_falloff(ps->brush, dist) * projPixel->mask;
if (alpha >= 0.0f) {
IMB_blend_color_float( projPixel->pixel.f_pt, projPixel->pixel.f_pt, ((ProjPixelCloneFloat *)projPixel)->clonepx_f, alpha, blend);
@ -3207,7 +3391,7 @@ static void *do_projectpaint_thread(void *ph_v)
}
} else {
if (((ProjPixelClone*)projPixel)->clonepx.ch[3]) {
alpha = brush_sample_falloff(ps->brush, dist);
alpha = brush_sample_falloff(ps->brush, dist) * projPixel->mask;
if (alpha > 0.0f) {
*projPixel->pixel.uint_pt = IMB_blend_color( *projPixel->pixel.uint_pt, ((ProjPixelClone*)projPixel)->clonepx.uint, (int)(alpha*255), blend);
@ -3219,7 +3403,8 @@ static void *do_projectpaint_thread(void *ph_v)
Vec2Subf(co, projPixel->projCo2D, pos_ofs);
if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1)) { /* Note, no interpolation here, only needed for clone, nearest should be is OK??? - c */
brush_sample_tex(ps->brush, projPixel->projCo2D, rgba);
alpha = rgba[3]*brush_sample_falloff(ps->brush, dist);
alpha = rgba[3]*brush_sample_falloff(ps->brush, dist) * projPixel->mask;
/* drat! - this could almost be very simple if we ignore
* the fact that applying the color directly gives feedback,
* instead, collect the colors and apply after :/ */
@ -3243,7 +3428,8 @@ static void *do_projectpaint_thread(void *ph_v)
break;
default:
brush_sample_tex(ps->brush, projPixel->projCo2D, rgba);
alpha = rgba[3]*brush_sample_falloff(ps->brush, dist);
alpha = rgba[3]*brush_sample_falloff(ps->brush, dist) * projPixel->mask;
if (alpha > 0.0f) {
if (is_floatbuf) {
rgba[0] *= ps->brush->rgb[0];
@ -3648,6 +3834,7 @@ void imagepaint_paint(short mousebutton, short texpaint)
/* setup projection painting data */
ps.do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1;
ps.do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1;
ps.do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? 0 : 1;;
if (settings->imapaint.flag & IMAGEPAINT_PROJECT_CLONE_LAYER) {
ps.clone_layer = settings->imapaint.clone_layer;
} else {