2013-03-06 19:54:43 +00:00
/*
* * * * * * BEGIN GPL LICENSE BLOCK * * * * *
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* The Original Code is Copyright ( C ) 2001 - 2002 by NaN Holding BV .
* All rights reserved .
*
* The Original Code is : some of this file .
*
* Contributor ( s ) : Jens Ole Wund ( bjornmose ) , Campbell Barton ( ideasman42 )
*
* * * * * * END GPL LICENSE BLOCK * * * * *
*/
2013-03-08 04:00:06 +00:00
/** \file blender/editors/sculpt_paint/paint_image_proj.c
2013-03-06 19:54:43 +00:00
* \ ingroup edsculpt
* \ brief Functions to paint images in 2 D and 3 D .
*/
# include <float.h>
# include <string.h>
# include <stdio.h>
# include <math.h>
# include "MEM_guardedalloc.h"
# ifdef WIN32
# include "BLI_winstuff.h"
# endif
# include "BLI_blenlib.h"
# include "BLI_linklist.h"
2013-04-28 11:55:41 +00:00
# include "BLI_math.h"
# include "BLI_math_color_blend.h"
2013-03-06 19:54:43 +00:00
# include "BLI_memarena.h"
# include "BLI_threads.h"
# include "BLI_utildefines.h"
2014-07-21 12:02:05 +02:00
# include "BLF_translation.h"
2013-03-06 19:54:43 +00:00
# include "IMB_imbuf.h"
# include "IMB_imbuf_types.h"
# include "DNA_brush_types.h"
2014-07-21 12:02:05 +02:00
# include "DNA_material_types.h"
2013-03-06 19:54:43 +00:00
# include "DNA_mesh_types.h"
# include "DNA_node_types.h"
# include "DNA_object_types.h"
# include "BKE_camera.h"
# include "BKE_context.h"
2014-07-21 12:02:05 +02:00
# include "BKE_depsgraph.h"
2013-03-06 19:54:43 +00:00
# include "BKE_DerivedMesh.h"
# include "BKE_idprop.h"
# include "BKE_brush.h"
# include "BKE_image.h"
# include "BKE_library.h"
# include "BKE_main.h"
2014-07-21 12:02:05 +02:00
# include "BKE_material.h"
# include "BKE_mesh.h"
2014-04-01 10:08:54 +11:00
# include "BKE_mesh_mapping.h"
2014-10-06 15:12:06 +02:00
# include "BKE_node.h"
2013-03-06 19:54:43 +00:00
# include "BKE_paint.h"
# include "BKE_report.h"
# include "BKE_scene.h"
2014-07-21 12:02:05 +02:00
# include "BKE_texture.h"
2013-03-06 19:54:43 +00:00
2014-07-21 12:02:05 +02:00
# include "UI_interface.h"
2013-03-06 19:54:43 +00:00
2014-09-05 15:48:03 +02:00
# include "ED_mesh.h"
2014-10-06 15:12:06 +02:00
# include "ED_node.h"
2014-06-23 21:59:34 +10:00
# include "ED_paint.h"
2013-03-06 19:54:43 +00:00
# include "ED_screen.h"
# include "ED_uvedit.h"
# include "ED_view3d.h"
2013-04-12 17:56:07 +00:00
# include "GPU_extensions.h"
2013-03-06 19:54:43 +00:00
# include "WM_api.h"
# include "WM_types.h"
# include "RNA_access.h"
# include "RNA_define.h"
# include "RNA_enum_types.h"
# include "GPU_draw.h"
# include "IMB_colormanagement.h"
2014-10-31 14:37:36 +01:00
# include "bmesh.h"
//#include "bmesh_tools.h"
2013-03-06 19:54:43 +00:00
# include "paint_intern.h"
/* Defines and Structs */
/* FTOCHAR as inline function */
BLI_INLINE unsigned char f_to_char ( const float val )
{
return FTOCHAR ( val ) ;
}
/* ProjectionPaint defines */
/* approx the number of buckets to have under the brush,
* used with the brush size to set the ps - > buckets_x and ps - > buckets_y value .
*
* When 3 - a brush should have ~ 9 buckets under it at once
* . . . this helps for threading while painting as well as
* avoiding initializing pixels that wont touch the brush */
# define PROJ_BUCKET_BRUSH_DIV 4
# define PROJ_BUCKET_RECT_MIN 4
# define PROJ_BUCKET_RECT_MAX 256
# define PROJ_BOUNDBOX_DIV 8
# 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
/* 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_SEAM1 (1 << 0) /* If this face has a seam on any of its edges */
# define PROJ_FACE_SEAM2 (1 << 1)
# define PROJ_FACE_SEAM3 (1 << 2)
# define PROJ_FACE_SEAM4 (1 << 3)
# define PROJ_FACE_NOSEAM1 (1 << 4)
# define PROJ_FACE_NOSEAM2 (1 << 5)
# define PROJ_FACE_NOSEAM3 (1 << 6)
# define PROJ_FACE_NOSEAM4 (1 << 7)
2014-05-21 18:35:08 +02:00
/* face winding */
# define PROJ_FACE_WINDING_INIT 1
# define PROJ_FACE_WINDING_CW 2
2013-03-06 19:54:43 +00:00
# define PROJ_SRC_VIEW 1
# define PROJ_SRC_IMAGE_CAM 2
# define PROJ_SRC_IMAGE_VIEW 3
2014-07-21 12:02:05 +02:00
# define PROJ_SRC_VIEW_FILL 4
2013-03-06 19:54:43 +00:00
# define PROJ_VIEW_DATA_ID "view_data"
# define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) /* viewmat + winmat + clipsta + clipend + is_ortho */
/* 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
# 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
2013-10-15 08:44:13 +00:00
# define PROJ_PIXEL_TOLERANCE 0.01f
2013-03-06 19:54:43 +00:00
/* vert flags */
# define PROJ_VERT_CULL 1
2014-07-21 12:02:05 +02:00
/* to avoid locking in tile initialization */
# define TILE_PENDING SET_INT_IN_POINTER(-1)
2013-03-06 19:54:43 +00:00
/* This is mainly a convenience struct used so we can keep an array of images we use
* Thir 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 ;
2014-07-21 12:02:05 +02:00
volatile void * * undoRect ; /* only used to build undo tiles during painting */
unsigned short * * maskRect ; /* the mask accumulation must happen on canvas, not on space screen bucket.
* Here we store the mask rectangle */
bool * * valid ; /* store flag to enforce validation of undo rectangle */
2013-03-06 19:54:43 +00:00
int touch ;
} ProjPaintImage ;
/* Main projection painting struct passed to all projection painting functions */
typedef struct ProjPaintState {
View3D * v3d ;
RegionView3D * rv3d ;
ARegion * ar ;
Scene * scene ;
int source ; /* PROJ_SRC_**** */
2014-07-21 12:02:05 +02:00
/* the paint color. It can change depending of inverted mode or not */
float paint_color [ 3 ] ;
float paint_color_linear [ 3 ] ;
2013-03-06 19:54:43 +00:00
Brush * brush ;
2013-03-17 03:08:46 +00:00
short tool , blend , mode ;
2013-03-10 17:40:55 +00:00
int orig_brush_size ;
2014-07-21 12:02:05 +02:00
float brush_size ;
2013-03-06 19:54:43 +00:00
Object * ob ;
/* end similarities with ImagePaintState */
DerivedMesh * dm ;
int dm_totface ;
int dm_totvert ;
int dm_release ;
MVert * dm_mvert ;
MFace * dm_mface ;
2014-07-21 12:02:05 +02:00
MTFace * * dm_mtface ;
MTFace * * dm_mtface_clone ; /* other UV map, use for cloning between layers */
2013-03-06 19:54:43 +00:00
MTFace * dm_mtface_stencil ;
2014-07-21 12:02:05 +02:00
Image * stencil_ima ;
2014-08-28 16:40:38 +02:00
Image * canvas_ima ;
Image * clone_ima ;
2014-07-21 12:02:05 +02:00
float stencil_value ;
2013-03-06 19:54:43 +00:00
/* projection painting only */
MemArena * arena_mt [ BLENDER_MAX_THREADS ] ; /* for multithreading, the first item is sometimes used for non threaded cases too */
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 */
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*/
2014-05-21 18:35:08 +02:00
char * faceWindingFlags ; /* save the winding of the face in uv space, helps as an extra validation step for seam detection */
2013-03-06 19:54:43 +00:00
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 */
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 ;
ProjPaintImage * projImages ;
int pixel_sizeof ; /* result of project_paint_pixel_sizeof(), constant per stroke */
int image_tot ; /* size of projectImages array */
float ( * screenCoords ) [ 4 ] ; /* verts projected into floating point screen space */
float screenMin [ 2 ] ; /* 2D bounds for mesh verts on the screen's plane (screenspace) */
float screenMax [ 2 ] ;
float screen_width ; /* Calculated from screenMin & screenMax */
float screen_height ;
int winx , winy ; /* from the carea or from the projection render */
/* options for projection painting */
2014-02-03 18:55:59 +11:00
bool do_layer_clone ;
bool do_layer_stencil ;
bool do_layer_stencil_inv ;
2014-07-21 12:02:05 +02:00
bool do_stencil_brush ;
2014-08-28 16:40:38 +02:00
bool do_material_slots ;
2014-02-03 18:55:59 +11:00
bool do_occlude ; /* Use raytraced occlusion? - ortherwise will paint right through to the back*/
bool do_backfacecull ; /* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */
bool do_mask_normal ; /* mask out pixels based on their normals */
bool do_new_shading_nodes ; /* cache BKE_scene_use_new_shading_nodes value */
2013-03-06 19:54:43 +00:00
float normal_angle ; /* what angle to mask at*/
float normal_angle_inner ;
float normal_angle_range ; /* difference between normal_angle and normal_angle_inner, for easy access */
2013-07-09 08:17:14 +00:00
bool do_face_sel ; /* quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */
bool is_ortho ;
2013-03-06 19:54:43 +00:00
bool do_masking ; /* use masking during painting. Some operations such as airbrush may disable */
2013-03-25 01:00:16 +00:00
bool is_texbrush ; /* only to avoid running */
2013-05-23 21:02:51 +00:00
bool is_maskbrush ; /* mask brush is applied before masking */
2013-03-06 19:54:43 +00:00
# ifndef PROJ_DEBUG_NOSEAMBLEED
float seam_bleed_px ;
# endif
/* clone vars */
float cloneOffset [ 2 ] ;
float projectMat [ 4 ] [ 4 ] ; /* Projection matrix, use for getting screen coords */
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 clipsta , clipend ;
/* reproject vars */
Image * reproject_image ;
ImBuf * reproject_ibuf ;
/* threads */
int thread_tot ;
int bucketMin [ 2 ] ;
int bucketMax [ 2 ] ;
int context_bucket_x , context_bucket_y ; /* must lock threads while accessing these */
2013-05-15 14:37:05 +00:00
/* redraw */
bool need_redraw ;
2014-07-21 12:02:05 +02:00
BlurKernel * blurkernel ;
SpinLock * tile_lock ;
2013-03-06 19:54:43 +00:00
} ProjPaintState ;
typedef union pixelPointer {
float * f_pt ; /* float buffer */
unsigned int * uint_pt ; /* 2 ways to access a char buffer */
unsigned char * ch_pt ;
} PixelPointer ;
typedef union pixelStore {
unsigned char ch [ 4 ] ;
unsigned int uint ;
float f [ 4 ] ;
} PixelStore ;
typedef struct ProjPixel {
float projCoSS [ 2 ] ; /* the floating point screen projection of this pixel */
float worldCoSS [ 3 ] ;
/* 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
2013-04-29 17:35:50 +00:00
* new mask value is lower then mask_accum */
2014-07-21 12:02:05 +02:00
unsigned short * mask_accum ;
2013-03-06 19:54:43 +00:00
/* for various reasons we may want to mask out painting onto this pixel */
unsigned short mask ;
short x_px , y_px ;
2014-07-21 12:02:05 +02:00
/* horrible hack, store tile valid flag pointer here to re-validate tiles used for anchored and drag-dot strokes */
bool * valid ;
2013-03-06 19:54:43 +00:00
2014-07-21 12:02:05 +02:00
PixelPointer origColor ;
2013-03-06 19:54:43 +00:00
PixelStore newColor ;
PixelPointer pixel ;
2013-04-07 15:09:06 +00:00
short image_index ; /* if anyone wants to paint onto more than 32768 images they can bite me */
2013-03-06 19:54:43 +00:00
unsigned char bb_cell_index ;
} ProjPixel ;
typedef struct ProjPixelClone {
struct ProjPixel __pp ;
PixelStore clonepx ;
} ProjPixelClone ;
2014-07-21 12:02:05 +02:00
/* undo tile pushing */
typedef struct {
SpinLock * lock ;
bool masked ;
unsigned short tile_width ;
ImBuf * * tmpibuf ;
ProjPaintImage * pjima ;
} TileInfo ;
2013-03-06 19:54:43 +00:00
/* Finish projection painting structs */
2014-07-21 12:02:05 +02:00
static TexPaintSlot * project_paint_face_paint_slot ( const ProjPaintState * ps , int face_index )
2013-03-06 19:54:43 +00:00
{
2014-07-21 12:02:05 +02:00
MFace * mf = ps - > dm_mface + face_index ;
Material * ma = ps - > dm - > mat [ mf - > mat_nr ] ;
2014-10-06 15:12:06 +02:00
return ma ? ma - > texpaintslot + ma - > paint_active_slot : NULL ;
2014-07-21 12:02:05 +02:00
}
2013-03-06 19:54:43 +00:00
2014-07-21 12:02:05 +02:00
static Image * project_paint_face_paint_image ( const ProjPaintState * ps , int face_index )
{
if ( ps - > do_stencil_brush ) {
return ps - > stencil_ima ;
2013-03-06 19:54:43 +00:00
}
else {
2014-07-21 12:02:05 +02:00
MFace * mf = ps - > dm_mface + face_index ;
Material * ma = ps - > dm - > mat [ mf - > mat_nr ] ;
2014-10-06 15:12:06 +02:00
TexPaintSlot * slot = ma ? ma - > texpaintslot + ma - > paint_active_slot : NULL ;
2014-08-28 16:40:38 +02:00
return slot ? slot - > ima : ps - > canvas_ima ;
2013-03-06 19:54:43 +00:00
}
2014-07-21 12:02:05 +02:00
}
static TexPaintSlot * project_paint_face_clone_slot ( const ProjPaintState * ps , int face_index )
{
MFace * mf = ps - > dm_mface + face_index ;
Material * ma = ps - > dm - > mat [ mf - > mat_nr ] ;
2014-10-06 15:12:06 +02:00
return ma ? ma - > texpaintslot + ma - > paint_clone_slot : NULL ;
2014-08-28 16:40:38 +02:00
}
static Image * project_paint_face_clone_image ( const ProjPaintState * ps , int face_index )
{
MFace * mf = ps - > dm_mface + face_index ;
Material * ma = ps - > dm - > mat [ mf - > mat_nr ] ;
2014-10-06 15:12:06 +02:00
TexPaintSlot * slot = ma ? ma - > texpaintslot + ma - > paint_clone_slot : NULL ;
2014-08-28 16:40:38 +02:00
return slot ? slot - > ima : ps - > clone_ima ;
2013-03-06 19:54:43 +00:00
}
/* 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 ) ;
}
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 ;
}
}
static float VecZDepthOrtho ( 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 ] ) ;
}
static float VecZDepthPersp ( 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 4 th 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 mimicing 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) */
2013-03-11 16:23:33 +00:00
static int project_paint_PickFace ( const ProjPaintState * ps , const float pt [ 2 ] , float w [ 3 ] , int * side )
2013-03-06 19:54:43 +00:00
{
LinkNode * node ;
float w_tmp [ 3 ] ;
2014-04-27 00:22:49 +10:00
const float * v1 , * v2 , * v3 , * v4 ;
2013-03-06 19:54:43 +00:00
int bucket_index ;
int face_index ;
int best_side = - 1 ;
int best_face_index = - 1 ;
float z_depth_best = FLT_MAX , z_depth ;
MFace * mf ;
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 ) {
face_index = GET_INT_FROM_POINTER ( node - > link ) ;
mf = ps - > dm_mface + face_index ;
v1 = ps - > screenCoords [ mf - > v1 ] ;
v2 = ps - > screenCoords [ mf - > v2 ] ;
v3 = ps - > screenCoords [ mf - > v3 ] ;
if ( isect_point_tri_v2 ( pt , v1 , v2 , v3 ) ) {
if ( ps - > is_ortho ) z_depth = VecZDepthOrtho ( pt , v1 , v2 , v3 , w_tmp ) ;
else z_depth = VecZDepthPersp ( pt , v1 , v2 , v3 , w_tmp ) ;
if ( z_depth < z_depth_best ) {
best_face_index = face_index ;
best_side = 0 ;
z_depth_best = z_depth ;
copy_v3_v3 ( w , w_tmp ) ;
}
}
else if ( mf - > v4 ) {
v4 = ps - > screenCoords [ mf - > v4 ] ;
if ( isect_point_tri_v2 ( pt , v1 , v3 , v4 ) ) {
if ( ps - > is_ortho ) z_depth = VecZDepthOrtho ( pt , v1 , v3 , v4 , w_tmp ) ;
else z_depth = VecZDepthPersp ( pt , v1 , v3 , v4 , w_tmp ) ;
if ( z_depth < z_depth_best ) {
best_face_index = face_index ;
best_side = 1 ;
z_depth_best = z_depth ;
copy_v3_v3 ( w , w_tmp ) ;
}
}
}
}
* side = best_side ;
return best_face_index ; /* will be -1 or a valid face */
}
/* Converts a uv coord into a pixel location wrapping if the uv is outside 0-1 range */
2013-05-08 12:54:33 +00:00
static void uvco_to_wrapped_pxco ( const float uv [ 2 ] , int ibuf_x , int ibuf_y , float * x , float * y )
2013-03-06 19:54:43 +00:00
{
/* use */
2014-07-21 12:02:05 +02:00
* x = fmodf ( uv [ 0 ] , 1.0f ) ;
* y = fmodf ( uv [ 1 ] , 1.0f ) ;
2013-03-06 19:54:43 +00:00
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 ;
}
/* Set the top-most face color that the screen space coord 'pt' touches (or return 0 if none touch) */
2013-10-15 07:35:12 +00:00
static bool project_paint_PickColor ( const ProjPaintState * ps , const float pt [ 2 ] ,
float * rgba_fp , unsigned char * rgba , const bool interp )
2013-03-06 19:54:43 +00:00
{
float w [ 3 ] , uv [ 2 ] ;
int side ;
int face_index ;
MTFace * tf ;
Image * ima ;
ImBuf * ibuf ;
int xi , yi ;
face_index = project_paint_PickFace ( ps , pt , w , & side ) ;
if ( face_index = = - 1 )
return 0 ;
2014-07-21 12:02:05 +02:00
tf = * ( ps - > dm_mtface + face_index ) ;
2013-03-06 19:54:43 +00:00
if ( side = = 0 ) {
interp_v2_v2v2v2 ( uv , tf - > uv [ 0 ] , tf - > uv [ 1 ] , tf - > uv [ 2 ] , w ) ;
}
else { /* QUAD */
interp_v2_v2v2v2 ( uv , tf - > uv [ 0 ] , tf - > uv [ 2 ] , tf - > uv [ 3 ] , w ) ;
}
2014-07-21 12:02:05 +02:00
ima = project_paint_face_paint_image ( ps , face_index ) ;
2013-12-13 16:22:08 +06:00
ibuf = BKE_image_get_first_ibuf ( ima ) ; /* we must have got the imbuf before getting here */
2014-07-21 12:02:05 +02:00
if ( ! ibuf ) return 0 ;
2013-03-06 19:54:43 +00:00
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 ) ;
2013-04-28 11:55:41 +00:00
premul_float_to_straight_uchar ( rgba , rgba_tmp_f ) ;
2013-03-06 19:54:43 +00:00
}
}
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 ) ;
2013-04-28 11:55:41 +00:00
straight_uchar_to_premul_float ( rgba_fp , rgba_tmp ) ;
2013-03-06 19:54:43 +00:00
}
}
}
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 */
2013-10-15 07:35:12 +00:00
xi = mod_i ( ( int ) ( uv [ 0 ] * ibuf - > x ) , ibuf - > x ) ;
yi = mod_i ( ( int ) ( uv [ 1 ] * ibuf - > y ) , ibuf - > y ) ;
2013-03-06 19:54:43 +00:00
if ( rgba ) {
if ( ibuf - > rect_float ) {
2014-04-27 00:22:49 +10:00
const float * rgba_tmp_fp = ibuf - > rect_float + ( xi + yi * ibuf - > x * 4 ) ;
2013-04-28 11:55:41 +00:00
premul_float_to_straight_uchar ( rgba , rgba_tmp_fp ) ;
2013-03-06 19:54:43 +00:00
}
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 {
2013-04-28 11:55:41 +00:00
unsigned char * tmp_ch = ( ( unsigned char * ) ibuf - > rect ) + ( ( xi + yi * ibuf - > x ) * 4 ) ;
straight_uchar_to_premul_float ( rgba_fp , tmp_ch ) ;
2013-03-06 19:54:43 +00:00
}
}
}
2014-01-03 23:00:53 +06:00
BKE_image_release_ibuf ( ima , ibuf , NULL ) ;
2013-03-06 19:54:43 +00:00
return 1 ;
}
/* Check if 'pt' is infront of the 3 verts on the Z axis (used for screenspace occlusuion test)
* return . . .
* 0 : no occlusion
* - 1 : no occlusion but 2 D intersection is true ( avoid testing the other half of a quad )
* 1 : occluded
* 2 : occluded with w [ 3 ] weights set ( need to know in some cases ) */
2013-10-15 07:18:23 +00:00
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 )
2013-03-06 19:54:43 +00:00
{
/* 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 ; /* we know there is */
/* 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 ProjPaintState * ps , const MFace * mf ,
2013-10-15 07:18:23 +00:00
const float pt [ 3 ] , const float v1 [ 4 ] , const float v2 [ 4 ] , const float v3 [ 4 ] ,
2013-03-06 19:54:43 +00:00
const int side )
{
float w [ 3 ] , wco [ 3 ] ;
int ret = project_paint_occlude_ptv ( pt , v1 , v2 , v3 , w , ps - > is_ortho ) ;
if ( ret < = 0 )
return ret ;
if ( ret = = 1 ) { /* weights not calculated */
if ( ps - > 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, */
if ( side ) interp_v3_v3v3v3 ( wco , ps - > dm_mvert [ mf - > v1 ] . co , ps - > dm_mvert [ mf - > v3 ] . co , ps - > dm_mvert [ mf - > v4 ] . co , w ) ;
else interp_v3_v3v3v3 ( wco , ps - > dm_mvert [ mf - > v1 ] . co , ps - > dm_mvert [ mf - > v2 ] . co , ps - > dm_mvert [ mf - > v3 ] . co , w ) ;
2014-04-01 11:34:00 +11:00
if ( ! ED_view3d_clipping_test ( ps - > rv3d , wco , true ) ) {
2013-03-06 19:54:43 +00:00
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 ) */
2013-10-15 07:35:12 +00:00
static bool project_bucket_point_occluded ( const ProjPaintState * ps , LinkNode * bucketFace ,
const int orig_face , const float pixelScreenCo [ 4 ] )
2013-03-06 19:54:43 +00:00
{
MFace * mf ;
int face_index ;
int isect_ret ;
float w [ 3 ] ; /* not needed when clipping */
const short do_clip = ps - > rv3d ? ps - > rv3d - > rflag & RV3D_CLIPPING : 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 ) {
face_index = GET_INT_FROM_POINTER ( bucketFace - > link ) ;
if ( orig_face ! = face_index ) {
mf = ps - > dm_mface + face_index ;
if ( do_clip )
isect_ret = project_paint_occlude_ptv_clip ( ps , mf , pixelScreenCo , ps - > screenCoords [ mf - > v1 ] , ps - > screenCoords [ mf - > v2 ] , ps - > screenCoords [ mf - > v3 ] , 0 ) ;
else
isect_ret = project_paint_occlude_ptv ( pixelScreenCo , ps - > screenCoords [ mf - > v1 ] , ps - > screenCoords [ mf - > v2 ] , ps - > screenCoords [ mf - > v3 ] , w , ps - > is_ortho ) ;
/* Note, if (isect_ret == -1) then we don't want to test the other side of the quad */
if ( isect_ret = = 0 & & mf - > v4 ) {
if ( do_clip )
isect_ret = project_paint_occlude_ptv_clip ( ps , mf , pixelScreenCo , ps - > screenCoords [ mf - > v1 ] , ps - > screenCoords [ mf - > v3 ] , ps - > screenCoords [ mf - > v4 ] , 1 ) ;
else
isect_ret = project_paint_occlude_ptv ( pixelScreenCo , ps - > screenCoords [ mf - > v1 ] , ps - > screenCoords [ mf - > v3 ] , ps - > screenCoords [ mf - > v4 ] , 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 */
2013-10-15 07:35:12 +00:00
return true ;
2013-03-06 19:54:43 +00:00
}
}
}
2013-10-15 07:35:12 +00:00
return false ;
2013-03-06 19:54:43 +00:00
}
/* basic line intersection, could move to math_geom.c, 2 points with a horiz line
* 1 for an intersection , 2 if the first point is aligned , 3 if the second point is aligned */
# define ISECT_TRUE 1
# define ISECT_TRUE_P1 2
# 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 ;
if ( y_level = = p1 [ 1 ] ) { /* are we touching the first point? - no interpolation needed */
* x_isect = p1 [ 0 ] ;
return ISECT_TRUE_P1 ;
}
if ( y_level = = p2 [ 1 ] ) { /* are we touching the second point? - no interpolation needed */
* x_isect = p2 [ 0 ] ;
return ISECT_TRUE_P2 ;
}
y_diff = fabsf ( p1 [ 1 ] - p2 [ 1 ] ) ; /* yuck, horizontal line, we cant do much here */
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 ) {
* x_isect = ( p2 [ 0 ] * ( p1 [ 1 ] - y_level ) + p1 [ 0 ] * ( y_level - p2 [ 1 ] ) ) / y_diff ; /*(p1[1]-p2[1]);*/
return ISECT_TRUE ;
}
else if ( p1 [ 1 ] < y_level & & p2 [ 1 ] > y_level ) {
* x_isect = ( p2 [ 0 ] * ( y_level - p1 [ 1 ] ) + p1 [ 0 ] * ( p2 [ 1 ] - y_level ) ) / y_diff ; /*(p2[1]-p1[1]);*/
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 ;
}
x_diff = fabsf ( p1 [ 0 ] - p2 [ 0 ] ) ; /* yuck, horizontal line, we cant do much here */
if ( x_diff < 0.000001f ) { /* yuck, vertical line, we cant do much here */
* y_isect = ( p1 [ 0 ] + p2 [ 0 ] ) * 0.5f ;
return ISECT_TRUE ;
}
if ( p1 [ 0 ] > x_level & & p2 [ 0 ] < x_level ) {
* y_isect = ( p2 [ 1 ] * ( p1 [ 0 ] - x_level ) + p1 [ 1 ] * ( x_level - p2 [ 0 ] ) ) / x_diff ; /*(p1[0]-p2[0]);*/
return ISECT_TRUE ;
}
else if ( p1 [ 0 ] < x_level & & p2 [ 0 ] > x_level ) {
* y_isect = ( p2 [ 1 ] * ( x_level - p1 [ 0 ] ) + p1 [ 1 ] * ( p2 [ 0 ] - x_level ) ) / x_diff ; /*(p2[0]-p1[0]);*/
return ISECT_TRUE ;
}
else {
return 0 ;
}
}
/* simple func use for comparing UV locations to check if there are seams.
* Its possible this gives incorrect results , when the UVs for 1 face go into the next
* tile , but do not do this for the adjacent face , it could return a false positive .
* This is so unlikely that Id not worry about it . */
# ifndef PROJ_DEBUG_NOSEAMBLEED
2013-10-15 07:35:12 +00:00
static bool cmp_uv ( const float vec2a [ 2 ] , const float vec2b [ 2 ] )
2013-03-06 19:54:43 +00:00
{
/* if the UV's are not between 0.0 and 1.0 */
2014-07-21 12:02:05 +02:00
float xa = fmodf ( vec2a [ 0 ] , 1.0f ) ;
float ya = fmodf ( vec2a [ 1 ] , 1.0f ) ;
2013-03-06 19:54:43 +00:00
2014-07-21 12:02:05 +02:00
float xb = fmodf ( vec2b [ 0 ] , 1.0f ) ;
float yb = fmodf ( vec2b [ 1 ] , 1.0f ) ;
2013-03-06 19:54:43 +00:00
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 ;
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
2013-10-15 07:35:12 +00:00
static bool pixel_bounds_uv (
2013-03-06 19:54:43 +00:00
const float uv1 [ 2 ] , const float uv2 [ 2 ] , const float uv3 [ 2 ] , const float uv4 [ 2 ] ,
rcti * bounds_px ,
const int ibuf_x , const int ibuf_y ,
2013-10-15 07:18:23 +00:00
const bool is_quad
2013-03-06 19:54:43 +00:00
)
{
float min_uv [ 2 ] , max_uv [ 2 ] ; /* UV bounds */
INIT_MINMAX2 ( min_uv , max_uv ) ;
minmax_v2v2_v2 ( min_uv , max_uv , uv1 ) ;
minmax_v2v2_v2 ( min_uv , max_uv , uv2 ) ;
minmax_v2v2_v2 ( min_uv , max_uv , uv3 ) ;
if ( is_quad )
minmax_v2v2_v2 ( min_uv , max_uv , uv4 ) ;
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 ;
/*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 ;
}
# endif
2013-10-15 07:35:12 +00:00
static bool pixel_bounds_array ( float ( * uv ) [ 2 ] , rcti * bounds_px , const int ibuf_x , const int ibuf_y , int tot )
2013-03-06 19:54:43 +00:00
{
float min_uv [ 2 ] , max_uv [ 2 ] ; /* UV bounds */
if ( tot = = 0 ) {
return 0 ;
}
INIT_MINMAX2 ( min_uv , max_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 - > 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]);*/
/* 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
2014-05-21 18:35:08 +02:00
static void project_face_winding_init ( const ProjPaintState * ps , const int face_index )
{
/* detect the winding of faces in uv space */
2014-07-21 12:02:05 +02:00
MTFace * tf = ps - > dm_mtface [ face_index ] ;
2014-05-21 18:52:50 +02:00
float winding = cross_tri_v2 ( tf - > uv [ 0 ] , tf - > uv [ 1 ] , tf - > uv [ 2 ] ) ;
2014-05-21 18:35:08 +02:00
if ( ps - > dm_mface [ face_index ] . v4 )
2014-05-21 18:52:50 +02:00
winding + = cross_tri_v2 ( tf - > uv [ 2 ] , tf - > uv [ 3 ] , tf - > uv [ 0 ] ) ;
2014-05-21 18:35:08 +02:00
if ( winding > 0 )
ps - > faceWindingFlags [ face_index ] | = PROJ_FACE_WINDING_CW ;
ps - > faceWindingFlags [ face_index ] | = PROJ_FACE_WINDING_INIT ;
}
2013-03-06 19:54:43 +00:00
/* This function returns 1 if this face has a seam along the 2 face-vert indices
* ' orig_i1_fidx ' and ' orig_i2_fidx ' */
2013-10-15 07:35:12 +00:00
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 )
2013-03-06 19:54:43 +00:00
{
LinkNode * node ;
int face_index ;
unsigned int i1 , i2 ;
int i1_fidx = - 1 , i2_fidx = - 1 ; /* index in face */
MFace * mf ;
MTFace * tf ;
const MFace * orig_mf = ps - > dm_mface + orig_face ;
2014-07-21 12:02:05 +02:00
const MTFace * orig_tf = ps - > dm_mtface [ orig_face ] ;
2013-03-06 19:54:43 +00:00
/* vert indices from face vert order indices */
i1 = ( * ( & orig_mf - > v1 + orig_i1_fidx ) ) ;
i2 = ( * ( & orig_mf - > v1 + orig_i2_fidx ) ) ;
for ( node = ps - > vertFaces [ i1 ] ; node ; node = node - > next ) {
face_index = GET_INT_FROM_POINTER ( node - > link ) ;
if ( face_index ! = orig_face ) {
mf = ps - > dm_mface + face_index ;
/* 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 ) */
2014-04-01 10:08:54 +11:00
i1_fidx = BKE_MESH_TESSFACE_VINDEX_ORDER ( mf , i1 ) ;
i2_fidx = BKE_MESH_TESSFACE_VINDEX_ORDER ( mf , i2 ) ;
2013-03-06 19:54:43 +00:00
/* 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 ) {
2014-07-21 12:02:05 +02:00
Image * tpage = project_paint_face_paint_image ( ps , face_index ) ;
Image * orig_tpage = project_paint_face_paint_image ( ps , orig_face ) ;
2013-03-06 19:54:43 +00:00
2014-04-01 10:08:54 +11:00
BLI_assert ( i1_fidx ! = - 1 ) ;
2013-03-06 19:54:43 +00:00
/* This IS an adjacent face!, now lets check if the UVs are ok */
2014-07-21 12:02:05 +02:00
tf = ps - > dm_mtface [ face_index ] ;
2013-03-06 19:54:43 +00:00
/* set up the other face */
* other_face = face_index ;
2014-09-01 12:35:10 +02:00
/* we check if difference is 1 here, else we might have a case of edge 2-0 or 3-0 for quads */
* orig_fidx = ( i1_fidx < i2_fidx & & ( i2_fidx - i1_fidx = = 1 ) ) ? i1_fidx : i2_fidx ;
2013-03-06 19:54:43 +00:00
2014-05-21 18:35:08 +02:00
/* initialize face winding if needed */
2014-05-31 17:29:02 +10:00
if ( ( ps - > faceWindingFlags [ face_index ] & PROJ_FACE_WINDING_INIT ) = = 0 )
2014-05-21 18:35:08 +02:00
project_face_winding_init ( ps , face_index ) ;
2013-03-06 19:54:43 +00:00
/* first test if they have the same image */
if ( ( orig_tpage = = tpage ) & &
cmp_uv ( orig_tf - > uv [ orig_i1_fidx ] , tf - > uv [ i1_fidx ] ) & &
2014-07-21 12:02:05 +02:00
cmp_uv ( orig_tf - > uv [ orig_i2_fidx ] , tf - > uv [ i2_fidx ] ) )
2013-03-06 19:54:43 +00:00
{
2014-05-21 18:35:08 +02:00
/* if faces don't have the same winding in uv space,
* they are on the same side so edge is boundary */
if ( ( ps - > faceWindingFlags [ face_index ] & PROJ_FACE_WINDING_CW ) ! =
( ps - > faceWindingFlags [ orig_face ] & PROJ_FACE_WINDING_CW ) )
{
return 1 ;
}
2013-03-06 19:54:43 +00:00
// printf("SEAM (NONE)\n");
return 0 ;
}
else {
// printf("SEAM (UV GAP)\n");
return 1 ;
}
}
}
}
// printf("SEAM (NO FACE)\n");
* other_face = - 1 ;
return 1 ;
}
2014-10-30 22:31:03 +01:00
# define SMALL_NUMBER 1.e-6f
BLI_INLINE float shell_v2v2_normal_dir_to_dist ( float n [ 2 ] , float d [ 2 ] )
{
const float angle_cos = ( normalize_v2 ( n ) < SMALL_NUMBER ) ? fabsf ( dot_v2v2 ( d , n ) ) : 0.0f ;
return ( UNLIKELY ( angle_cos < SMALL_NUMBER ) ) ? 1.0f : ( 1.0f / angle_cos ) ;
}
# undef SMALL_NUMBER
2013-03-06 19:54:43 +00:00
/* 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 */
2013-03-11 16:23:33 +00:00
static void uv_image_outset ( float ( * orig_uv ) [ 2 ] , float ( * outset_uv ) [ 2 ] , const float scaler ,
2014-10-30 21:47:25 +01:00
const int ibuf_x , const int ibuf_y , const bool is_quad , const bool cw )
2013-03-06 19:54:43 +00:00
{
float a1 , a2 , a3 , a4 = 0.0f ;
float puv [ 4 ] [ 2 ] ; /* pixelspace uv's */
float no1 [ 2 ] , no2 [ 2 ] , no3 [ 2 ] , no4 [ 2 ] ; /* normals */
float dir1 [ 2 ] , dir2 [ 2 ] , dir3 [ 2 ] , dir4 [ 2 ] ;
float ibuf_inv [ 2 ] ;
ibuf_inv [ 0 ] = 1.0f / ( float ) ibuf_x ;
ibuf_inv [ 1 ] = 1.0f / ( float ) ibuf_y ;
/* make UV's in pixel space so we can */
puv [ 0 ] [ 0 ] = orig_uv [ 0 ] [ 0 ] * ibuf_x ;
puv [ 0 ] [ 1 ] = orig_uv [ 0 ] [ 1 ] * ibuf_y ;
puv [ 1 ] [ 0 ] = orig_uv [ 1 ] [ 0 ] * ibuf_x ;
puv [ 1 ] [ 1 ] = orig_uv [ 1 ] [ 1 ] * ibuf_y ;
puv [ 2 ] [ 0 ] = orig_uv [ 2 ] [ 0 ] * ibuf_x ;
puv [ 2 ] [ 1 ] = orig_uv [ 2 ] [ 1 ] * ibuf_y ;
if ( is_quad ) {
puv [ 3 ] [ 0 ] = orig_uv [ 3 ] [ 0 ] * ibuf_x ;
puv [ 3 ] [ 1 ] = orig_uv [ 3 ] [ 1 ] * ibuf_y ;
}
/* face edge directions */
sub_v2_v2v2 ( dir1 , puv [ 1 ] , puv [ 0 ] ) ;
sub_v2_v2v2 ( dir2 , puv [ 2 ] , puv [ 1 ] ) ;
normalize_v2 ( dir1 ) ;
normalize_v2 ( dir2 ) ;
if ( is_quad ) {
sub_v2_v2v2 ( dir3 , puv [ 3 ] , puv [ 2 ] ) ;
sub_v2_v2v2 ( dir4 , puv [ 0 ] , puv [ 3 ] ) ;
normalize_v2 ( dir3 ) ;
normalize_v2 ( dir4 ) ;
}
else {
sub_v2_v2v2 ( dir3 , puv [ 0 ] , puv [ 2 ] ) ;
normalize_v2 ( dir3 ) ;
}
if ( is_quad ) {
2014-11-14 12:08:41 +01:00
/* here we just use the orthonormality property (a1, a2) dot (a2, -a1) = 0
* to get normals from the edge directions based on the winding */
2014-10-30 21:47:25 +01:00
if ( cw ) {
no1 [ 0 ] = - dir4 [ 1 ] - dir1 [ 1 ] ;
no1 [ 1 ] = dir4 [ 0 ] + dir1 [ 0 ] ;
no2 [ 0 ] = - dir1 [ 1 ] - dir2 [ 1 ] ;
no2 [ 1 ] = dir1 [ 0 ] + dir2 [ 0 ] ;
no3 [ 0 ] = - dir2 [ 1 ] - dir3 [ 1 ] ;
no3 [ 1 ] = dir2 [ 0 ] + dir3 [ 0 ] ;
no4 [ 0 ] = - dir3 [ 1 ] - dir4 [ 1 ] ;
no4 [ 1 ] = dir3 [ 0 ] + dir4 [ 0 ] ;
}
else {
no1 [ 0 ] = dir4 [ 1 ] + dir1 [ 1 ] ;
no1 [ 1 ] = - dir4 [ 0 ] - dir1 [ 0 ] ;
no2 [ 0 ] = dir1 [ 1 ] + dir2 [ 1 ] ;
no2 [ 1 ] = - dir1 [ 0 ] - dir2 [ 0 ] ;
no3 [ 0 ] = dir2 [ 1 ] + dir3 [ 1 ] ;
no3 [ 1 ] = - dir2 [ 0 ] - dir3 [ 0 ] ;
no4 [ 0 ] = dir3 [ 1 ] + dir4 [ 1 ] ;
no4 [ 1 ] = - dir3 [ 0 ] - dir4 [ 0 ] ;
}
2014-10-30 22:31:03 +01:00
a1 = shell_v2v2_normal_dir_to_dist ( no1 , dir4 ) ;
a2 = shell_v2v2_normal_dir_to_dist ( no2 , dir1 ) ;
a3 = shell_v2v2_normal_dir_to_dist ( no3 , dir2 ) ;
a4 = shell_v2v2_normal_dir_to_dist ( no4 , dir3 ) ;
2014-10-30 21:47:25 +01:00
2013-03-06 19:54:43 +00:00
mul_v2_fl ( no1 , a1 * scaler ) ;
mul_v2_fl ( no2 , a2 * scaler ) ;
mul_v2_fl ( no3 , a3 * scaler ) ;
mul_v2_fl ( no4 , a4 * scaler ) ;
add_v2_v2v2 ( outset_uv [ 0 ] , puv [ 0 ] , no1 ) ;
add_v2_v2v2 ( outset_uv [ 1 ] , puv [ 1 ] , no2 ) ;
add_v2_v2v2 ( outset_uv [ 2 ] , puv [ 2 ] , no3 ) ;
add_v2_v2v2 ( outset_uv [ 3 ] , puv [ 3 ] , no4 ) ;
mul_v2_v2 ( outset_uv [ 0 ] , ibuf_inv ) ;
mul_v2_v2 ( outset_uv [ 1 ] , ibuf_inv ) ;
mul_v2_v2 ( outset_uv [ 2 ] , ibuf_inv ) ;
mul_v2_v2 ( outset_uv [ 3 ] , ibuf_inv ) ;
}
else {
2014-10-30 21:47:25 +01:00
if ( cw ) {
no1 [ 0 ] = - dir3 [ 1 ] - dir1 [ 1 ] ;
no1 [ 1 ] = dir3 [ 0 ] + dir1 [ 0 ] ;
no2 [ 0 ] = - dir1 [ 1 ] - dir2 [ 1 ] ;
no2 [ 1 ] = dir1 [ 0 ] + dir2 [ 0 ] ;
no3 [ 0 ] = - dir2 [ 1 ] - dir3 [ 1 ] ;
no3 [ 1 ] = dir2 [ 0 ] + dir3 [ 0 ] ;
}
else {
no1 [ 0 ] = dir3 [ 1 ] + dir1 [ 1 ] ;
no1 [ 1 ] = - dir3 [ 0 ] - dir1 [ 0 ] ;
no2 [ 0 ] = dir1 [ 1 ] + dir2 [ 1 ] ;
no2 [ 1 ] = - dir1 [ 0 ] - dir2 [ 0 ] ;
no3 [ 0 ] = dir2 [ 1 ] + dir3 [ 1 ] ;
no3 [ 1 ] = - dir2 [ 0 ] - dir3 [ 0 ] ;
}
2014-10-30 22:31:03 +01:00
a1 = shell_v2v2_normal_dir_to_dist ( no1 , dir3 ) ;
a2 = shell_v2v2_normal_dir_to_dist ( no2 , dir1 ) ;
a3 = shell_v2v2_normal_dir_to_dist ( no3 , dir2 ) ;
2014-10-30 21:47:25 +01:00
2013-03-06 19:54:43 +00:00
mul_v2_fl ( no1 , a1 * scaler ) ;
mul_v2_fl ( no2 , a2 * scaler ) ;
mul_v2_fl ( no3 , a3 * scaler ) ;
add_v2_v2v2 ( outset_uv [ 0 ] , puv [ 0 ] , no1 ) ;
add_v2_v2v2 ( outset_uv [ 1 ] , puv [ 1 ] , no2 ) ;
add_v2_v2v2 ( outset_uv [ 2 ] , puv [ 2 ] , no3 ) ;
mul_v2_v2 ( outset_uv [ 0 ] , ibuf_inv ) ;
mul_v2_v2 ( outset_uv [ 1 ] , ibuf_inv ) ;
mul_v2_v2 ( outset_uv [ 2 ] , ibuf_inv ) ;
}
}
/*
* Be tricky with flags , first 4 bits are PROJ_FACE_SEAM1 to 4 , last 4 bits are PROJ_FACE_NOSEAM1 to 4
* 1 < < i - where i is ( 0 - 3 )
*
* If we ' re multithreadng , make sure threads are locked when this is called
*/
static void project_face_seams_init ( const ProjPaintState * ps , const int face_index , const int is_quad )
{
int other_face , other_fidx ; /* vars for the other face, we also set its flag */
int fidx1 = is_quad ? 3 : 2 ;
int fidx2 = 0 ; /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */
2014-05-21 18:35:08 +02:00
/* initialize face winding if needed */
2014-05-31 17:29:02 +10:00
if ( ( ps - > faceWindingFlags [ face_index ] & PROJ_FACE_WINDING_INIT ) = = 0 )
2014-05-21 18:35:08 +02:00
project_face_winding_init ( ps , face_index ) ;
2013-03-06 19:54:43 +00:00
do {
if ( ( ps - > faceSeamFlags [ face_index ] & ( 1 < < fidx1 | 16 < < fidx1 ) ) = = 0 ) {
if ( check_seam ( ps , face_index , fidx1 , fidx2 , & other_face , & other_fidx ) ) {
ps - > faceSeamFlags [ face_index ] | = 1 < < fidx1 ;
if ( other_face ! = - 1 )
ps - > faceSeamFlags [ other_face ] | = 1 < < other_fidx ;
}
else {
ps - > faceSeamFlags [ face_index ] | = 16 < < fidx1 ;
if ( other_face ! = - 1 )
ps - > faceSeamFlags [ other_face ] | = 16 < < other_fidx ; /* second 4 bits for disabled */
}
}
fidx2 = fidx1 ;
} while ( fidx1 - - ) ;
}
# 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 (
2014-04-27 07:50:08 +10:00
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 ] ,
2013-03-06 19:54:43 +00:00
float pixelScreenCo [ 4 ] ,
float w [ 3 ] )
{
barycentric_weights_v2 ( uv1co , uv2co , uv3co , uv , w ) ;
interp_v3_v3v3v3 ( pixelScreenCo , v1co , v2co , v3co , w ) ;
}
2014-04-29 16:52:32 +03:00
/* same as screen_px_from_ortho except we
* do perspective correction on the pixel coordinate */
2013-03-06 19:54:43 +00:00
static void screen_px_from_persp (
2014-04-27 07:50:08 +10:00
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 ] ,
2013-03-06 19:54:43 +00:00
float pixelScreenCo [ 4 ] ,
float w [ 3 ] )
{
2013-06-26 14:28:39 +00:00
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 ] =
w_int [ 0 ] = w_int [ 1 ] = w_int [ 2 ] = 1.0f / 3.0f ; /* dummy values for zero area face */
}
/* done re-weighting */
/* do interpolation based on projected weight */
interp_v3_v3v3v3 ( pixelScreenCo , v1co , v2co , v3co , w_int ) ;
}
2013-03-11 16:23:33 +00:00
static void project_face_pixel ( const MTFace * tf_other , ImBuf * ibuf_other , const float w [ 3 ] ,
int side , unsigned char rgba_ub [ 4 ] , float rgba_f [ 4 ] )
2013-03-06 19:54:43 +00:00
{
2014-04-27 00:22:49 +10:00
const float * uvCo1 , * uvCo2 , * uvCo3 ;
2013-03-06 19:54:43 +00:00
float uv_other [ 2 ] , x , y ;
uvCo1 = ( float * ) tf_other - > uv [ 0 ] ;
if ( side = = 1 ) {
uvCo2 = ( float * ) tf_other - > uv [ 2 ] ;
uvCo3 = ( float * ) tf_other - > uv [ 3 ] ;
}
else {
uvCo2 = ( float * ) tf_other - > uv [ 1 ] ;
uvCo3 = ( float * ) tf_other - > uv [ 2 ] ;
}
interp_v2_v2v2v2 ( uv_other , uvCo1 , uvCo2 , uvCo3 , ( float * ) w ) ;
/* 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 face_index ,
const int side ,
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 ;
2014-07-21 12:02:05 +02:00
Image * other_tpage = ps - > stencil_ima ;
2013-03-06 19:54:43 +00:00
const MTFace * tf_other = ps - > dm_mtface_stencil + face_index ;
if ( other_tpage & & ( ibuf_other = BKE_image_acquire_ibuf ( other_tpage , NULL , NULL ) ) ) {
/* BKE_image_acquire_ibuf - TODO - this may be slow */
unsigned char rgba_ub [ 4 ] ;
float rgba_f [ 4 ] ;
project_face_pixel ( tf_other , ibuf_other , w , side , rgba_ub , rgba_f ) ;
if ( ibuf_other - > rect_float ) { /* from float to float */
2013-04-28 11:55:41 +00:00
mask = ( ( rgba_f [ 0 ] + rgba_f [ 1 ] + rgba_f [ 2 ] ) * ( 1.0f / 3.0f ) ) * rgba_f [ 3 ] ;
2013-03-06 19:54:43 +00:00
}
else { /* from char to float */
2013-04-28 11:55:41 +00:00
mask = ( ( rgba_ub [ 0 ] + rgba_ub [ 1 ] + rgba_ub [ 2 ] ) * ( 1.0f / ( 255.0f * 3.0f ) ) ) * ( rgba_ub [ 3 ] * ( 1.0f / 255.0f ) ) ;
2013-03-06 19:54:43 +00:00
}
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 ;
}
/* calculate mask */
if ( ps - > do_mask_normal ) {
MFace * mf = & ps - > dm_mface [ face_index ] ;
float no [ 3 ] , angle ;
if ( mf - > flag & ME_SMOOTH ) {
2014-04-27 00:22:49 +10:00
const short * no1 , * no2 , * no3 ;
2013-03-06 19:54:43 +00:00
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_v3 ( no ) ;
}
else {
/* incase the */
# if 1
/* normalizing per pixel isn't optimal, we could cache or check ps->*/
if ( mf - > v4 )
normal_quad_v3 ( no ,
ps - > dm_mvert [ mf - > v1 ] . co ,
ps - > dm_mvert [ mf - > v2 ] . co ,
ps - > dm_mvert [ mf - > v3 ] . co ,
ps - > dm_mvert [ mf - > v4 ] . co ) ;
else
normal_tri_v3 ( no ,
ps - > dm_mvert [ mf - > v1 ] . co ,
ps - > dm_mvert [ mf - > v2 ] . co ,
ps - > dm_mvert [ mf - > v3 ] . 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 , face_index , CD_NORMAL ) ) ;
# endif
}
/* now we can use the normal as a mask */
if ( ps - > is_ortho ) {
angle = angle_normalized_v3v3 ( ( float * ) ps - > viewDir , no ) ;
}
else {
/* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */
float viewDirPersp [ 3 ] ;
2014-04-27 00:22:49 +10:00
const float * co1 , * co2 , * co3 ;
2013-03-06 19:54:43 +00:00
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 ;
}
/* 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 ) ;
angle = angle_normalized_v3v3 ( viewDirPersp , no ) ;
}
if ( angle > = ps - > normal_angle ) {
return 0.0f ; /* outsize the normal limit*/
}
else if ( angle > ps - > normal_angle_inner ) {
mask * = ( ps - > normal_angle - angle ) / ps - > normal_angle_range ;
} /* otherwise no mask normal is needed, were within the limit */
}
/* This only works when the opacity dosnt 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 ) ;
}
}
2014-07-21 12:02:05 +02:00
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 ) {
volatile void * undorect ;
if ( tinf - > masked ) {
undorect = image_undo_push_tile ( pjIma - > ima , pjIma - > ibuf , tinf - > tmpibuf , tx , ty , & pjIma - > maskRect [ tile_index ] , & pjIma - > valid [ tile_index ] , true ) ;
}
else {
undorect = image_undo_push_tile ( pjIma - > ima , pjIma - > ibuf , tinf - > tmpibuf , tx , ty , NULL , & pjIma - > valid [ tile_index ] , true ) ;
}
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 ;
}
2013-03-06 19:54:43 +00:00
/* 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 ,
2014-07-21 12:02:05 +02:00
const TileInfo * tinf ,
2013-10-15 07:35:12 +00:00
int x_px , int y_px ,
2013-03-06 19:54:43 +00:00
const float mask ,
const int face_index ,
const float pixelScreenCo [ 4 ] ,
const float world_spaceCo [ 3 ] ,
const int side ,
const float w [ 3 ] )
{
ProjPixel * projPixel ;
2014-07-21 12:02:05 +02:00
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 ;
2013-03-06 19:54:43 +00:00
/* wrap pixel location */
2013-10-15 07:35:12 +00:00
x_px = mod_i ( x_px , ibuf - > x ) ;
y_px = mod_i ( y_px , ibuf - > y ) ;
2013-03-06 19:54:43 +00:00
BLI_assert ( ps - > pixel_sizeof = = project_paint_pixel_sizeof ( ps - > tool ) ) ;
projPixel = ( ProjPixel * ) BLI_memarena_alloc ( arena , ps - > pixel_sizeof ) ;
2014-07-21 12:02:05 +02:00
/* 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 ;
2013-03-06 19:54:43 +00:00
//memset(projPixel, 0, size);
2014-07-21 12:02:05 +02:00
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 ] ;
2013-03-06 19:54:43 +00:00
if ( ibuf - > rect_float ) {
projPixel - > pixel . f_pt = ibuf - > rect_float + ( ( x_px + y_px * ibuf - > x ) * 4 ) ;
2014-07-21 12:02:05 +02:00
projPixel - > origColor . f_pt = ( float * ) projima - > undoRect [ tile_index ] + 4 * tile_offset ;
2013-04-28 11:55:41 +00:00
zero_v4 ( projPixel - > newColor . f ) ;
2013-03-06 19:54:43 +00:00
}
else {
2014-07-21 12:02:05 +02:00
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 ;
2013-04-28 11:55:41 +00:00
projPixel - > newColor . uint = 0 ;
2013-03-06 19:54:43 +00:00
}
/* 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 ) ;
2014-07-21 12:02:05 +02:00
if ( ps - > do_masking )
projPixel - > mask_accum = projima - > maskRect [ tile_index ] + tile_offset ;
else
projPixel - > mask_accum = NULL ;
2013-03-06 19:54:43 +00:00
/* 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 - > dm_mtface_clone ) {
ImBuf * ibuf_other ;
2014-08-28 16:40:38 +02:00
Image * other_tpage = project_paint_face_clone_image ( ps , face_index ) ;
2014-07-21 12:02:05 +02:00
const MTFace * tf_other = ps - > dm_mtface_clone [ face_index ] ;
2013-03-06 19:54:43 +00:00
if ( other_tpage & & ( ibuf_other = BKE_image_acquire_ibuf ( other_tpage , NULL , NULL ) ) ) {
/* 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 ( tf_other , ibuf_other , w , side , NULL , ( ( ProjPixelClone * ) projPixel ) - > clonepx . f ) ;
}
else { /* from char to float */
unsigned char rgba_ub [ 4 ] ;
2013-03-11 18:11:07 +00:00
float rgba [ 4 ] ;
2013-03-06 19:54:43 +00:00
project_face_pixel ( tf_other , ibuf_other , w , side , rgba_ub , NULL ) ;
2013-03-11 18:11:07 +00:00
srgb_to_linearrgb_uchar4 ( rgba , rgba_ub ) ;
straight_to_premul_v4_v4 ( ( ( ProjPixelClone * ) projPixel ) - > clonepx . f , rgba ) ;
2013-03-06 19:54:43 +00:00
}
}
else {
if ( ibuf_other - > rect_float ) { /* float to char */
float rgba [ 4 ] ;
project_face_pixel ( tf_other , ibuf_other , w , side , NULL , rgba ) ;
2013-03-11 18:11:07 +00:00
premul_to_straight_v4 ( rgba ) ;
linearrgb_to_srgb_uchar3 ( ( ( ProjPixelClone * ) projPixel ) - > clonepx . ch , rgba ) ;
2014-10-17 14:11:13 +02:00
( ( ProjPixelClone * ) projPixel ) - > clonepx . ch [ 3 ] = rgba [ 3 ] * 255 ;
2013-03-06 19:54:43 +00:00
}
else { /* char to char */
project_face_pixel ( tf_other , ibuf_other , w , side , ( ( 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 , ( float * ) 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 ) ) {
( ( ProjPixelClone * ) 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 ) projPixel - > pixel . f_pt [ 0 ] = 0 ;
else projPixel - > pixel . ch_pt [ 0 ] = 0 ;
# endif
2014-07-21 12:02:05 +02:00
/* pointer arithmetics */
projPixel - > image_index = projima - ps - > projImages ;
2013-03-06 19:54:43 +00:00
return projPixel ;
}
2013-10-15 07:35:12 +00:00
static bool line_clip_rect2f (
2013-03-06 19:54:43 +00:00
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_GEOM_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 ;
}
if ( fabsf ( l1 [ 0 ] - l2 [ 0 ] ) < PROJ_GEOM_TOLERANCE ) { /* this is a single point (or close to)*/
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_GEOM_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 ;
}
if ( fabsf ( l1 [ 1 ] - l2 [ 1 ] ) < PROJ_GEOM_TOLERANCE ) { /* this is a single point (or close to)*/
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 > = rect - > xmin ) & & ( isect < = rect - > 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 > = rect - > xmin ) & & ( isect < = rect - > 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 > = rect - > ymin ) & & ( isect < = rect - > 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 > = rect - > ymin ) & & ( isect < = rect - > 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 quad & tri about its center
* scaling by PROJ_FACE_SCALE_SEAM ( 0.99 x ) is used for getting fake UV pixel coords that are on the
* edge of the face but slightly inside it occlusion tests don ' t return hits on adjacent faces */
# ifndef PROJ_DEBUG_NOSEAMBLEED
static void scale_quad ( float insetCos [ 4 ] [ 3 ] , float * origCos [ 4 ] , const float inset )
{
float cent [ 3 ] ;
2013-04-28 11:55:41 +00:00
cent [ 0 ] = ( origCos [ 0 ] [ 0 ] + origCos [ 1 ] [ 0 ] + origCos [ 2 ] [ 0 ] + origCos [ 3 ] [ 0 ] ) * ( 1.0f / 4.0f ) ;
cent [ 1 ] = ( origCos [ 0 ] [ 1 ] + origCos [ 1 ] [ 1 ] + origCos [ 2 ] [ 1 ] + origCos [ 3 ] [ 1 ] ) * ( 1.0f / 4.0f ) ;
cent [ 2 ] = ( origCos [ 0 ] [ 2 ] + origCos [ 1 ] [ 2 ] + origCos [ 2 ] [ 2 ] + origCos [ 3 ] [ 2 ] ) * ( 1.0f / 4.0f ) ;
2013-03-06 19:54:43 +00:00
sub_v3_v3v3 ( insetCos [ 0 ] , origCos [ 0 ] , cent ) ;
sub_v3_v3v3 ( insetCos [ 1 ] , origCos [ 1 ] , cent ) ;
sub_v3_v3v3 ( insetCos [ 2 ] , origCos [ 2 ] , cent ) ;
sub_v3_v3v3 ( insetCos [ 3 ] , origCos [ 3 ] , cent ) ;
mul_v3_fl ( insetCos [ 0 ] , inset ) ;
mul_v3_fl ( insetCos [ 1 ] , inset ) ;
mul_v3_fl ( insetCos [ 2 ] , inset ) ;
mul_v3_fl ( insetCos [ 3 ] , inset ) ;
add_v3_v3 ( insetCos [ 0 ] , cent ) ;
add_v3_v3 ( insetCos [ 1 ] , cent ) ;
add_v3_v3 ( insetCos [ 2 ] , cent ) ;
add_v3_v3 ( insetCos [ 3 ] , cent ) ;
}
static void scale_tri ( float insetCos [ 4 ] [ 3 ] , float * origCos [ 4 ] , const float inset )
{
float cent [ 3 ] ;
2013-04-28 11:55:41 +00:00
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 ) ;
2013-03-06 19:54:43 +00:00
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
2014-03-30 11:08:33 +11:00
static float len_squared_v2v2_alt ( const float v1 [ 2 ] , const float v2_1 , const float v2_2 )
2013-03-06 19:54:43 +00:00
{
float x , 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 */
2013-10-15 07:35:12 +00:00
static bool project_bucket_isect_circle ( const float cent [ 2 ] , const float radius_squared , rctf * bucket_bounds )
2013-03-06 19:54:43 +00:00
{
/* 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 ;
# 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 ;
}
/* 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
* ( viewed from the side )
*
* for this reason its not reliable in this case so we ' ll use the Simple Barycentric '
* 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 (
rctf * bucket_bounds ,
2014-04-27 07:50:08 +10:00
const float * v1coSS , const float * v2coSS , const float * v3coSS ,
const float * uv1co , const float * uv2co , const float * uv3co ,
2013-03-06 19:54:43 +00:00
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 ) ;
}
/* same as above but use barycentric_weights_v2_persp */
static void rect_to_uvspace_persp (
rctf * bucket_bounds ,
2014-04-27 07:50:08 +10:00
const float * v1coSS , const float * v2coSS , const float * v3coSS ,
const float * uv1co , const float * uv2co , const float * uv3co ,
2013-03-06 19:54:43 +00:00
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 ) ;
}
/* This works as we need it to but we can save a few steps and not use it */
#if 0
static float angle_2d_clockwise ( const float p1 [ 2 ] , const float p2 [ 2 ] , const float p3 [ 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 ] ;
return - atan2 ( v1 [ 0 ] * v2 [ 1 ] - v1 [ 1 ] * v2 [ 0 ] , v1 [ 0 ] * v2 [ 0 ] + v1 [ 1 ] * v2 [ 1 ] ) ;
}
# endif
# define ISECT_1 (1)
# define ISECT_2 (1 << 1)
# define ISECT_3 (1 << 2)
# define ISECT_4 (1 << 3)
# define ISECT_ALL3 ((1 << 3) - 1)
# define ISECT_ALL4 ((1 << 4) - 1)
/* limit must be a fraction over 1.0f */
2013-10-15 07:35:12 +00:00
static bool IsectPT2Df_limit ( float pt [ 2 ] , float v1 [ 2 ] , float v2 [ 2 ] , float v3 [ 2 ] , float limit )
2013-03-06 19:54:43 +00:00
{
2013-10-15 07:35:12 +00:00
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 ;
2013-03-06 19:54:43 +00:00
}
/* Clip the face by a bucket and set the uv-space bucket_bounds_uv
* so we have the clipped UV ' s to do pixel intersection tests with
* */
static int float_z_sort_flip ( const void * p1 , const void * p2 )
{
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 ) ;
}
2014-12-19 12:27:29 +01:00
/* assumes one point is within the rectangle */
static void line_rect_clip (
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 ;
if ( ( l1 [ 0 ] - rect - > xmin ) * ( l2 [ 0 ] - rect - > xmin ) < 0 ) {
tmp = rect - > xmin ;
min = min_ff ( ( tmp - l1 [ 0 ] ) / ( l2 [ 0 ] - l1 [ 0 ] ) , min ) ;
}
else if ( ( l1 [ 0 ] - rect - > xmax ) * ( l2 [ 0 ] - rect - > xmax ) < 0 ) {
tmp = rect - > xmax ;
min = min_ff ( ( tmp - l1 [ 0 ] ) / ( l2 [ 0 ] - l1 [ 0 ] ) , min ) ;
}
if ( ( l1 [ 1 ] - rect - > ymin ) * ( l2 [ 1 ] - rect - > ymin ) < 0 ) {
tmp = rect - > ymin ;
min = min_ff ( ( tmp - l1 [ 1 ] ) / ( l2 [ 1 ] - l1 [ 1 ] ) , min ) ;
}
else if ( ( l1 [ 1 ] - rect - > ymax ) * ( l2 [ 1 ] - rect - > ymax ) < 0 ) {
tmp = rect - > ymax ;
min = min_ff ( ( tmp - l1 [ 1 ] ) / ( l2 [ 1 ] - l1 [ 1 ] ) , min ) ;
}
2014-12-19 14:43:04 +01:00
tmp = ( is_ortho ) ? 1.0f : ( l1 [ 3 ] + min * ( l2 [ 3 ] - l1 [ 3 ] ) ) ;
2014-12-19 12:27:29 +01:00
2014-12-19 14:43:04 +01:00
uv [ 0 ] = ( uv1 [ 0 ] + min * ( uv2 [ 0 ] - uv1 [ 0 ] ) ) / tmp ;
uv [ 1 ] = ( uv1 [ 1 ] + min * ( uv2 [ 1 ] - uv1 [ 1 ] ) ) / tmp ;
2014-12-19 12:27:29 +01:00
}
2013-03-06 19:54:43 +00:00
static void project_bucket_clip_face (
2014-02-03 18:55:59 +11:00
const bool is_ortho ,
2013-03-06 19:54:43 +00:00
rctf * bucket_bounds ,
float * v1coSS , float * v2coSS , float * v3coSS ,
2014-04-27 07:50:08 +10:00
const float * uv1co , const float * uv2co , const float * uv3co ,
2013-03-06 19:54:43 +00:00
float bucket_bounds_uv [ 8 ] [ 2 ] ,
2014-12-19 12:27:29 +01:00
int * tot , bool cull )
2013-03-06 19:54:43 +00:00
{
int inside_bucket_flag = 0 ;
int inside_face_flag = 0 ;
const int flip = ( ( line_point_side_v2 ( v1coSS , v2coSS , v3coSS ) > 0.0f ) ! = ( line_point_side_v2 ( uv1co , uv2co , uv3co ) > 0.0f ) ) ;
2014-12-19 12:27:29 +01:00
bool colinear = false ;
2013-03-06 19:54:43 +00:00
float bucket_bounds_ss [ 4 ] [ 2 ] ;
2014-12-19 12:27:29 +01:00
/* detect pathological case where face the three vertices are almost colinear in screen space.
* mostly those will be culled but when flood filling or with smooth shading */
if ( dist_squared_to_line_v2 ( v1coSS , v2coSS , v3coSS ) < PROJ_PIXEL_TOLERANCE )
colinear = true ;
2013-03-06 19:54:43 +00:00
/* 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 ;
2014-12-19 12:27:29 +01:00
2013-03-06 19:54:43 +00:00
if ( inside_bucket_flag = = ISECT_ALL3 ) {
/* 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 ) ;
2014-12-19 12:27:29 +01:00
}
* tot = 3 ;
return ;
}
/* handle pathological case here, no need for further intersections below since tringle area is almost zero */
if ( colinear ) {
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 ) ) {
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 ) ) {
line_rect_clip ( bucket_bounds , v2coSS , v3coSS , uv2co , uv3co , bucket_bounds_uv [ * tot ] , is_ortho ) ;
( * tot ) + + ;
2013-03-06 19:54:43 +00:00
}
2014-12-19 12:27:29 +01:00
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 ) ) {
line_rect_clip ( bucket_bounds , v3coSS , v1coSS , uv3co , uv1co , bucket_bounds_uv [ * tot ] , is_ortho ) ;
( * tot ) + + ;
}
if ( ( * tot ) < 3 ) { /* no intersections to speak of */
* tot = 0 ;
return ;
}
/* at this point we have all uv points needed in a row. all that's needed is to invert them if necessary */
if ( flip ) {
/* flip only to the middle of the array */
int i , max = * tot / 2 ;
for ( i = 0 ; i < max ; i + + ) {
SWAP ( float , bucket_bounds_uv [ i ] [ 0 ] , bucket_bounds_uv [ max - i ] [ 0 ] ) ;
SWAP ( float , bucket_bounds_uv [ i ] [ 1 ] , bucket_bounds_uv [ max - i ] [ 1 ] ) ;
}
}
2013-03-06 19:54:43 +00:00
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 ) ;
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 */
float isectVCosSS [ 8 ] [ 3 ] ; /* The 3rd float is used to store angle for qsort(), NOT as a Z location */
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 ;
short 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 ( 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 ( 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 ( 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
v1_clipSS [ 0 ] = cent [ 0 ] ; /* Abuse this var for the loop below */
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 ) ;
2014-04-01 11:34:00 +11:00
doubles = true ;
while ( doubles = = true ) {
doubles = false ;
2014-12-19 12:27:29 +01:00
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 )
2013-03-06 19:54:43 +00:00
{
int j ;
2014-12-19 12:27:29 +01:00
for ( j = i ; j < ( * tot ) - 1 ; j + + ) {
isectVCosSS [ j ] [ 0 ] = isectVCosSS [ j + 1 ] [ 0 ] ;
isectVCosSS [ j ] [ 1 ] = isectVCosSS [ j + 1 ] [ 1 ] ;
2013-03-06 19:54:43 +00:00
}
2014-04-01 11:34:00 +11:00
doubles = true ; /* keep looking for more doubles */
2013-03-06 19:54:43 +00:00
( * tot ) - - ;
}
}
2014-12-19 12:27:29 +01:00
/* its possible there is only a few left after remove doubles */
if ( ( * tot ) < 3 ) {
// printf("removed too many doubles B\n");
* tot = 0 ;
return ;
}
2013-03-06 19:54:43 +00:00
}
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 " ) ;
}
# endif
}
/*
* # This script creates faces in a blender scene from printed data above .
*
* project_ls = [
* . . . ( output from above block ) . . .
* ]
*
* from Blender import Scene , Mesh , Window , sys , Mathutils
*
* import bpy
*
* V = Mathutils . Vector
*
* def main ( ) :
* sce = bpy . data . scenes . active
*
* for item in project_ls :
* bb = item [ 0 ]
* uv = item [ 1 ]
* poly = item [ 2 ]
*
* me = bpy . data . meshes . new ( )
* ob = sce . objects . new ( me )
*
* me . verts . extend ( [ V ( bb [ 0 ] ) . xyz , V ( bb [ 1 ] ) . xyz , V ( bb [ 2 ] ) . xyz , V ( bb [ 3 ] ) . xyz ] )
* me . faces . extend ( [ ( 0 , 1 , 2 , 3 ) , ] )
* me . verts . extend ( [ V ( uv [ 0 ] ) . xyz , V ( uv [ 1 ] ) . xyz , V ( uv [ 2 ] ) . xyz ] )
* me . faces . extend ( [ ( 4 , 5 , 6 ) , ] )
*
* vs = [ V ( p ) . xyz for p in poly ]
* print len ( vs )
* l = len ( me . verts )
* me . verts . extend ( vs )
*
* i = l
* while i < len ( me . verts ) :
* ii = i + 1
* if ii = = len ( me . verts ) :
* ii = l
* me . edges . extend ( [ i , ii ] )
* i + = 1
*
* if __name__ = = ' __main__ ' :
* main ( )
*/
# undef ISECT_1
# undef ISECT_2
# undef ISECT_3
# undef ISECT_4
# 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 */
2013-10-15 07:35:12 +00:00
static bool IsectPoly2Df ( const float pt [ 2 ] , float uv [ ] [ 2 ] , const int tot )
2013-03-06 19:54:43 +00:00
{
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 ;
}
return 1 ;
}
2013-10-15 07:35:12 +00:00
static bool IsectPoly2Df_twoside ( const float pt [ 2 ] , float uv [ ] [ 2 ] , const int tot )
2013-03-06 19:54:43 +00:00
{
int i ;
int 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 ;
}
/* One of the most important function for projection painting, since it selects the pixels to be added into each bucket.
* initialize pixels from this face where it intersects with the bucket_index , optionally initialize pixels for removing seams */
2014-07-21 12:02:05 +02:00
static void project_paint_face_init ( const ProjPaintState * ps , const int thread_index , const int bucket_index , const int face_index , const int image_index , rctf * bucket_bounds , ImBuf * ibuf , ImBuf * * tmpibuf , const short clamp_u , const short clamp_v )
2013-03-06 19:54:43 +00:00
{
/* 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 ] ;
2014-07-21 12:02:05 +02:00
bool threaded = ( ps - > thread_tot > 1 ) ;
TileInfo tinf = {
ps - > tile_lock ,
ps - > do_masking ,
IMAPAINT_TILE_NUMBER ( ibuf - > x ) ,
tmpibuf ,
ps - > projImages + image_index
} ;
2013-03-06 19:54:43 +00:00
const MFace * mf = ps - > dm_mface + face_index ;
2014-07-21 12:02:05 +02:00
const MTFace * tf = ps - > dm_mtface [ face_index ] ;
2013-03-06 19:54:43 +00:00
/* 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 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 */
float w [ 3 ] , wco [ 3 ] ;
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 ] ;
bool do_3d_mapping = ps - > brush - > mtex . brush_map_mode = = MTEX_MAP_MODE_3D ;
rcti bounds_px ; /* ispace bounds */
/* vars for getting uvspace bounds */
float tf_uv_pxoffset [ 4 ] [ 2 ] ; /* bucket bounds in UV space so we can init pixels only for this face, */
float xhalfpx , yhalfpx ;
const float ibuf_xf = ( float ) ibuf - > x , ibuf_yf = ( float ) ibuf - > y ;
int has_x_isect = 0 , has_isect = 0 ; /* for early loop exit */
int i1 , i2 , i3 ;
float uv_clip [ 8 ] [ 2 ] ;
int uv_clip_tot ;
2014-02-03 18:55:59 +11:00
const bool is_ortho = ps - > is_ortho ;
const bool do_backfacecull = ps - > do_backfacecull ;
const bool do_clip = ps - > rv3d ? ps - > rv3d - > rflag & RV3D_CLIPPING : 0 ;
2013-03-06 19:54:43 +00:00
vCo [ 0 ] = ps - > dm_mvert [ mf - > v1 ] . co ;
vCo [ 1 ] = ps - > dm_mvert [ mf - > v2 ] . co ;
vCo [ 2 ] = ps - > dm_mvert [ mf - > v3 ] . co ;
/* Use tf_uv_pxoffset instead of tf->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 */
2014-07-31 12:15:38 +02:00
xhalfpx = ( 0.5f + ( PROJ_PIXEL_TOLERANCE * ( 1.0f / 3.0f ) ) ) / ibuf_xf ;
yhalfpx = ( 0.5f + ( PROJ_PIXEL_TOLERANCE * ( 1.0f / 4.0f ) ) ) / ibuf_yf ;
2013-03-06 19:54:43 +00:00
/* 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 .
*/
tf_uv_pxoffset [ 0 ] [ 0 ] = tf - > uv [ 0 ] [ 0 ] - xhalfpx ;
tf_uv_pxoffset [ 0 ] [ 1 ] = tf - > uv [ 0 ] [ 1 ] - yhalfpx ;
tf_uv_pxoffset [ 1 ] [ 0 ] = tf - > uv [ 1 ] [ 0 ] - xhalfpx ;
tf_uv_pxoffset [ 1 ] [ 1 ] = tf - > uv [ 1 ] [ 1 ] - yhalfpx ;
tf_uv_pxoffset [ 2 ] [ 0 ] = tf - > uv [ 2 ] [ 0 ] - xhalfpx ;
tf_uv_pxoffset [ 2 ] [ 1 ] = tf - > uv [ 2 ] [ 1 ] - yhalfpx ;
if ( mf - > v4 ) {
vCo [ 3 ] = ps - > dm_mvert [ mf - > v4 ] . co ;
tf_uv_pxoffset [ 3 ] [ 0 ] = tf - > uv [ 3 ] [ 0 ] - xhalfpx ;
tf_uv_pxoffset [ 3 ] [ 1 ] = tf - > uv [ 3 ] [ 1 ] - yhalfpx ;
side = 1 ;
}
else {
side = 0 ;
}
do {
if ( side = = 1 ) {
i1 = 0 ; i2 = 2 ; i3 = 3 ;
}
else {
i1 = 0 ; i2 = 1 ; i3 = 2 ;
}
uv1co = tf_uv_pxoffset [ i1 ] ; // was tf->uv[i1];
uv2co = tf_uv_pxoffset [ i2 ] ; // was tf->uv[i2];
uv3co = tf_uv_pxoffset [ i3 ] ; // was tf->uv[i3];
v1coSS = ps - > screenCoords [ ( * ( & mf - > v1 + i1 ) ) ] ;
v2coSS = ps - > screenCoords [ ( * ( & mf - > v1 + i2 ) ) ] ;
v3coSS = ps - > screenCoords [ ( * ( & mf - > v1 + i3 ) ) ] ;
/* This funtion gives is a concave polyline in UV space from the clipped quad and tri*/
project_bucket_clip_face (
is_ortho , bucket_bounds ,
v1coSS , v2coSS , v3coSS ,
uv1co , uv2co , uv3co ,
2014-12-19 12:27:29 +01:00
uv_clip , & uv_clip_tot ,
ps - > do_backfacecull | | ps - > do_occlude ) ;
2013-03-06 19:54:43 +00:00
/* 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 ) ;
}
# endif
if ( pixel_bounds_array ( uv_clip , & bounds_px , ibuf - > x , ibuf - > y , uv_clip_tot ) ) {
if ( clamp_u ) {
CLAMP ( bounds_px . xmin , 0 , ibuf - > x ) ;
CLAMP ( bounds_px . xmax , 0 , ibuf - > x ) ;
}
if ( clamp_v ) {
CLAMP ( bounds_px . ymin , 0 , ibuf - > y ) ;
CLAMP ( bounds_px . ymax , 0 , ibuf - > y ) ;
}
2014-07-21 12:02:05 +02:00
/*
project_paint_undo_tiles_init ( & bounds_px , ps - > projImages + image_index , tmpibuf ,
tile_width , threaded , ps - > do_masking ) ;
*/
2013-03-06 19:54:43 +00:00
/* 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;
uv [ 1 ] = ( float ) y / ibuf_yf ; /* use pixel offset UV coords instead */
has_x_isect = 0 ;
for ( x = bounds_px . xmin ; x < bounds_px . xmax ; x + + ) {
//uv[0] = (((float)x) + 0.5f) / ibuf->x;
uv [ 0 ] = ( float ) x / ibuf_xf ; /* use pixel offset UV coords instead */
/* Note about IsectPoly2Df_twoside, checking the face or uv flipping doesnt work,
* could check the poly direction but better to do this */
2014-04-01 11:34:00 +11:00
if ( ( do_backfacecull = = true & & IsectPoly2Df ( uv , uv_clip , uv_clip_tot ) ) | |
( do_backfacecull = = false & & IsectPoly2Df_twoside ( uv , uv_clip , uv_clip_tot ) ) )
2013-03-06 19:54:43 +00:00
{
has_x_isect = has_isect = 1 ;
if ( is_ortho ) screen_px_from_ortho ( uv , v1coSS , v2coSS , v3coSS , uv1co , uv2co , uv3co , pixelScreenCo , w ) ;
2013-06-26 14:28:39 +00:00
else screen_px_from_persp ( uv , v1coSS , v2coSS , v3coSS , uv1co , uv2co , uv3co , pixelScreenCo , w ) ;
2013-03-06 19:54:43 +00:00
/* a pity we need to get the worldspace pixel location here */
if ( do_clip | | do_3d_mapping ) {
interp_v3_v3v3v3 ( wco , ps - > dm_mvert [ ( * ( & mf - > v1 + i1 ) ) ] . co , ps - > dm_mvert [ ( * ( & mf - > v1 + i2 ) ) ] . co , ps - > dm_mvert [ ( * ( & mf - > v1 + i3 ) ) ] . co , w ) ;
2014-04-01 11:34:00 +11:00
if ( do_clip & & ED_view3d_clipping_test ( ps - > rv3d , wco , true ) ) {
2013-03-06 19:54:43 +00:00
continue ; /* Watch out that no code below this needs to run */
}
}
/* 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) {
2014-04-01 11:34:00 +11:00
if ( ( ps - > do_occlude = = false ) | |
2013-03-06 19:54:43 +00:00
! 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 ,
2014-07-21 12:02:05 +02:00
project_paint_uvpixel_init ( ps , arena , & tinf , x , y , mask , face_index ,
pixelScreenCo , wco , side , w ) ,
2013-03-06 19:54:43 +00:00
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
}
}
} while ( side - - ) ;
# ifndef PROJ_DEBUG_NOSEAMBLEED
if ( ps - > seam_bleed_px > 0.0f ) {
int face_seam_flag ;
2014-07-21 12:02:05 +02:00
if ( threaded )
2013-03-06 19:54:43 +00:00
BLI_lock_thread ( LOCK_CUSTOM1 ) ; /* Other threads could be modifying these vars */
face_seam_flag = ps - > faceSeamFlags [ face_index ] ;
/* are any of our edges un-initialized? */
if ( ( face_seam_flag & ( PROJ_FACE_SEAM1 | PROJ_FACE_NOSEAM1 ) ) = = 0 | |
( face_seam_flag & ( PROJ_FACE_SEAM2 | PROJ_FACE_NOSEAM2 ) ) = = 0 | |
( face_seam_flag & ( PROJ_FACE_SEAM3 | PROJ_FACE_NOSEAM3 ) ) = = 0 | |
( face_seam_flag & ( PROJ_FACE_SEAM4 | PROJ_FACE_NOSEAM4 ) ) = = 0 )
{
project_face_seams_init ( ps , face_index , mf - > v4 ) ;
face_seam_flag = ps - > faceSeamFlags [ face_index ] ;
//printf("seams - %d %d %d %d\n", flag&PROJ_FACE_SEAM1, flag&PROJ_FACE_SEAM2, flag&PROJ_FACE_SEAM3, flag&PROJ_FACE_SEAM4);
}
if ( ( face_seam_flag & ( PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2 | PROJ_FACE_SEAM3 | PROJ_FACE_SEAM4 ) ) = = 0 ) {
2014-07-21 12:02:05 +02:00
if ( threaded )
2013-03-06 19:54:43 +00:00
BLI_unlock_thread ( LOCK_CUSTOM1 ) ; /* Other threads could be modifying these vars */
}
else {
/* we have a seam - deal with it! */
/* Now create new UV's for the seam face */
float ( * outset_uv ) [ 2 ] = ps - > faceSeamUVs [ face_index ] ;
float insetCos [ 4 ] [ 3 ] ; /* inset face coords. NOTE!!! ScreenSace for ortho, Worldspace in prespective view */
float * vCoSS [ 4 ] ; /* vertex screenspace coords */
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 */
float seam_subsection [ 4 ] [ 2 ] ;
float fac1 , fac2 , ftot ;
if ( outset_uv [ 0 ] [ 0 ] = = FLT_MAX ) /* first time initialize */
2014-10-30 21:47:25 +01:00
uv_image_outset ( tf_uv_pxoffset , outset_uv , ps - > seam_bleed_px , ibuf - > x , ibuf - > y , mf - > v4 ! = 0 , ( ps - > faceWindingFlags [ face_index ] & PROJ_FACE_WINDING_CW ) = = 0 ) ;
2013-03-06 19:54:43 +00:00
/* ps->faceSeamUVs cant be modified when threading, now this is done we can unlock */
2014-07-21 12:02:05 +02:00
if ( threaded )
2013-03-06 19:54:43 +00:00
BLI_unlock_thread ( LOCK_CUSTOM1 ) ; /* Other threads could be modifying these vars */
vCoSS [ 0 ] = ps - > screenCoords [ mf - > v1 ] ;
vCoSS [ 1 ] = ps - > screenCoords [ mf - > v2 ] ;
vCoSS [ 2 ] = ps - > screenCoords [ mf - > v3 ] ;
if ( mf - > v4 )
vCoSS [ 3 ] = ps - > screenCoords [ mf - > v4 ] ;
/* PROJ_FACE_SCALE_SEAM must be slightly less then 1.0f */
if ( is_ortho ) {
if ( mf - > v4 ) scale_quad ( insetCos , vCoSS , PROJ_FACE_SCALE_SEAM ) ;
else scale_tri ( insetCos , vCoSS , PROJ_FACE_SCALE_SEAM ) ;
}
else {
if ( mf - > v4 ) scale_quad ( insetCos , vCo , PROJ_FACE_SCALE_SEAM ) ;
else scale_tri ( insetCos , vCo , PROJ_FACE_SCALE_SEAM ) ;
}
side = 0 ; /* for triangles this wont need to change */
for ( fidx1 = 0 ; fidx1 < ( mf - > v4 ? 4 : 3 ) ; fidx1 + + ) {
if ( mf - > v4 ) fidx2 = ( fidx1 = = 3 ) ? 0 : fidx1 + 1 ; /* next fidx in the face (0,1,2,3) -> (1,2,3,0) */
else fidx2 = ( fidx1 = = 2 ) ? 0 : fidx1 + 1 ; /* next fidx in the face (0,1,2) -> (1,2,0) */
if ( ( face_seam_flag & ( 1 < < fidx1 ) ) & & /* 1<<fidx1 -> PROJ_FACE_SEAM# */
line_clip_rect2f ( bucket_bounds , vCoSS [ fidx1 ] , vCoSS [ fidx2 ] , bucket_clip_edges [ 0 ] , bucket_clip_edges [ 1 ] ) )
{
ftot = len_v2v2 ( vCoSS [ fidx1 ] , vCoSS [ fidx2 ] ) ; /* screenspace edge length */
if ( ftot > 0.0f ) { /* avoid div by zero */
if ( mf - > v4 ) {
if ( fidx1 = = 2 | | fidx2 = = 2 ) side = 1 ;
else side = 0 ;
}
fac1 = len_v2v2 ( vCoSS [ fidx1 ] , bucket_clip_edges [ 0 ] ) / ftot ;
fac2 = len_v2v2 ( vCoSS [ fidx1 ] , bucket_clip_edges [ 1 ] ) / ftot ;
interp_v2_v2v2 ( seam_subsection [ 0 ] , tf_uv_pxoffset [ fidx1 ] , tf_uv_pxoffset [ fidx2 ] , fac1 ) ;
interp_v2_v2v2 ( seam_subsection [ 1 ] , tf_uv_pxoffset [ fidx1 ] , tf_uv_pxoffset [ fidx2 ] , fac2 ) ;
interp_v2_v2v2 ( seam_subsection [ 2 ] , outset_uv [ fidx1 ] , outset_uv [ fidx2 ] , fac2 ) ;
interp_v2_v2v2 ( seam_subsection [ 3 ] , outset_uv [ fidx1 ] , outset_uv [ fidx2 ] , 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 ) ;
2013-10-15 07:18:23 +00:00
if ( pixel_bounds_uv ( seam_subsection [ 0 ] , seam_subsection [ 1 ] , seam_subsection [ 2 ] , seam_subsection [ 3 ] , & bounds_px , ibuf - > x , ibuf - > y , true ) ) {
2013-03-06 19:54:43 +00:00
/* 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;
uv [ 1 ] = ( float ) y / ibuf_yf ; /* use offset uvs instead */
has_x_isect = 0 ;
for ( x = bounds_px . xmin ; x < bounds_px . xmax ; x + + ) {
//uv[0] = (((float)x) + 0.5f) / (float)ibuf->x;
uv [ 0 ] = ( float ) x / ibuf_xf ; /* use offset uvs instead */
/* test we're inside uvspace bucket and triangle bounds */
if ( isect_point_quad_v2 ( uv , seam_subsection [ 0 ] , seam_subsection [ 1 ] , seam_subsection [ 2 ] , seam_subsection [ 3 ] ) ) {
float fac ;
/* We need to find the closest point along the face edge,
* getting the screen_px_from_ * * * wont work because our actual location
* is not relevant , since we are outside the face , Use VecLerpf to find
* our location on the side of the face ' s UV */
#if 0
if ( is_ortho ) screen_px_from_ortho ( ps , uv , v1co , v2co , v3co , uv1co , uv2co , uv3co , pixelScreenCo ) ;
else screen_px_from_persp ( ps , uv , v1co , v2co , v3co , uv1co , uv2co , uv3co , pixelScreenCo ) ;
# endif
/* Since this is a seam we need to work out where on the line this pixel is */
//fac = line_point_factor_v2(uv, uv_seam_quad[0], uv_seam_quad[1]);
fac = line_point_factor_v2 ( uv , seam_subsection [ 0 ] , seam_subsection [ 1 ] ) ;
if ( fac < 0.0f ) { copy_v3_v3 ( pixelScreenCo , edge_verts_inset_clip [ 0 ] ) ; }
else if ( fac > 1.0f ) { copy_v3_v3 ( pixelScreenCo , edge_verts_inset_clip [ 1 ] ) ; }
else { interp_v3_v3v3 ( pixelScreenCo , edge_verts_inset_clip [ 0 ] , edge_verts_inset_clip [ 1 ] , fac ) ; }
if ( ! is_ortho ) {
pixelScreenCo [ 3 ] = 1.0f ;
mul_m4_v4 ( ( float ( * ) [ 4 ] ) ps - > projectMat , pixelScreenCo ) ; /* cast because of const */
2013-04-28 11:55:41 +00:00
pixelScreenCo [ 0 ] = ( float ) ( ps - > winx * 0.5f ) + ( ps - > winx * 0.5f ) * pixelScreenCo [ 0 ] / pixelScreenCo [ 3 ] ;
pixelScreenCo [ 1 ] = ( float ) ( ps - > winy * 0.5f ) + ( ps - > winy * 0.5f ) * pixelScreenCo [ 1 ] / pixelScreenCo [ 3 ] ;
2013-03-06 19:54:43 +00:00
pixelScreenCo [ 2 ] = pixelScreenCo [ 2 ] / pixelScreenCo [ 3 ] ; /* Use the depth for bucket point occlusion */
}
2014-04-01 11:34:00 +11:00
if ( ( ps - > do_occlude = = false ) | |
2013-03-06 19:54:43 +00:00
! 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 ) {
# if 1
/* get the UV on the line since we want to copy the pixels from there for bleeding */
float uv_close [ 2 ] ;
float uv_fac = closest_to_line_v2 ( uv_close , uv , tf_uv_pxoffset [ fidx1 ] , tf_uv_pxoffset [ fidx2 ] ) ;
if ( uv_fac < 0.0f ) copy_v2_v2 ( uv_close , tf_uv_pxoffset [ fidx1 ] ) ;
else if ( uv_fac > 1.0f ) copy_v2_v2 ( uv_close , tf_uv_pxoffset [ fidx2 ] ) ;
if ( side ) {
barycentric_weights_v2 ( tf_uv_pxoffset [ 0 ] , tf_uv_pxoffset [ 2 ] , tf_uv_pxoffset [ 3 ] , uv_close , w ) ;
}
else {
barycentric_weights_v2 ( tf_uv_pxoffset [ 0 ] , tf_uv_pxoffset [ 1 ] , tf_uv_pxoffset [ 2 ] , uv_close , w ) ;
}
# else /* this is buggy with quads, don't use for now */
/* Cheat, we know where we are along the edge so work out the weights from that */
uv_fac = fac1 + ( uv_fac * ( fac2 - fac1 ) ) ;
w [ 0 ] = w [ 1 ] = w [ 2 ] = 0.0 ;
if ( side ) {
w [ fidx1 ? fidx1 - 1 : 0 ] = 1.0f - uv_fac ;
w [ fidx2 ? fidx2 - 1 : 0 ] = uv_fac ;
}
else {
w [ fidx1 ] = 1.0f - uv_fac ;
w [ fidx2 ] = uv_fac ;
}
# endif
}
/* a pity we need to get the worldspace pixel location here */
if ( do_clip | | do_3d_mapping ) {
if ( side ) interp_v3_v3v3v3 ( wco , ps - > dm_mvert [ mf - > v1 ] . co , ps - > dm_mvert [ mf - > v3 ] . co , ps - > dm_mvert [ mf - > v4 ] . co , w ) ;
else interp_v3_v3v3v3 ( wco , ps - > dm_mvert [ mf - > v1 ] . co , ps - > dm_mvert [ mf - > v2 ] . co , ps - > dm_mvert [ mf - > v3 ] . co , w ) ;
2014-04-01 11:34:00 +11:00
if ( do_clip & & ED_view3d_clipping_test ( ps - > rv3d , wco , true ) ) {
2013-03-06 19:54:43 +00:00
continue ; /* Watch out that no code below this needs to run */
}
}
mask = project_paint_uvpixel_mask ( ps , face_index , side , w ) ;
if ( mask > 0.0f ) {
BLI_linklist_prepend_arena (
bucketPixelNodes ,
2014-07-21 12:02:05 +02:00
project_paint_uvpixel_init ( ps , arena , & tinf , x , y , mask , face_index ,
pixelScreenCo , wco , side , w ) ,
2013-03-06 19:54:43 +00:00
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
}
}
}
}
}
}
}
# 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 ] )
{
/* 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 */
bucketMin [ 0 ] = ( int ) ( ( int ) ( ( ( float ) ( min [ 0 ] - ps - > screenMin [ 0 ] ) / ps - > screen_width ) * ps - > buckets_x ) + 0.5f ) ; /* these offsets of 0.5 and 1.5 seem odd but they are correct */
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 )
{
bucket_bounds - > xmin = ps - > screenMin [ 0 ] + ( ( bucket_x ) * ( ps - > screen_width / ps - > buckets_x ) ) ; /* left */
bucket_bounds - > xmax = ps - > screenMin [ 0 ] + ( ( bucket_x + 1 ) * ( ps - > screen_width / ps - > buckets_x ) ) ; /* right */
bucket_bounds - > ymin = ps - > screenMin [ 1 ] + ( ( bucket_y ) * ( ps - > screen_height / ps - > buckets_y ) ) ; /* bottom */
bucket_bounds - > ymax = ps - > screenMin [ 1 ] + ( ( bucket_y + 1 ) * ( ps - > screen_height / ps - > buckets_y ) ) ; /* top */
}
/* 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 , rctf * bucket_bounds )
{
LinkNode * node ;
int face_index , image_index = 0 ;
ImBuf * ibuf = NULL ;
Image * tpage_last = NULL , * tpage ;
Image * ima = NULL ;
2014-07-21 12:02:05 +02:00
ImBuf * tmpibuf = NULL ;
2013-03-06 19:54:43 +00:00
if ( ps - > image_tot = = 1 ) {
/* Simple loop, no context switching */
ibuf = ps - > projImages [ 0 ] . ibuf ;
ima = ps - > projImages [ 0 ] . ima ;
for ( node = ps - > bucketFaces [ bucket_index ] ; node ; node = node - > next ) {
2014-07-21 12:02:05 +02:00
project_paint_face_init ( ps , thread_index , bucket_index , GET_INT_FROM_POINTER ( node - > link ) , 0 , bucket_bounds , ibuf , & tmpibuf , ima - > tpageflag & IMA_CLAMP_U , ima - > tpageflag & IMA_CLAMP_V ) ;
2013-03-06 19:54:43 +00:00
}
}
else {
/* More complicated loop, switch between images */
for ( node = ps - > bucketFaces [ bucket_index ] ; node ; node = node - > next ) {
face_index = GET_INT_FROM_POINTER ( node - > link ) ;
/* Image context switching */
2014-07-21 12:02:05 +02:00
tpage = project_paint_face_paint_image ( ps , face_index ) ;
2013-03-06 19:54:43 +00:00
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 ;
ima = ps - > projImages [ image_index ] . ima ;
break ;
}
}
}
/* context switching done */
2014-07-21 12:02:05 +02:00
project_paint_face_init ( ps , thread_index , bucket_index , face_index , image_index , bucket_bounds , ibuf , & tmpibuf , ima - > tpageflag & IMA_CLAMP_U , ima - > tpageflag & IMA_CLAMP_V ) ;
2013-03-06 19:54:43 +00:00
}
}
2014-07-21 12:02:05 +02:00
if ( tmpibuf )
IMB_freeImBuf ( tmpibuf ) ;
2013-03-06 19:54:43 +00:00
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 */
2013-10-15 07:35:12 +00:00
static bool project_bucket_face_isect ( ProjPaintState * ps , int bucket_x , int bucket_y , const MFace * mf )
2013-03-06 19:54:43 +00:00
{
/* TODO - replace this with a tricker method that uses sideofline for all screenCoords's edges against the closest bucket corner */
rctf bucket_bounds ;
float p1 [ 2 ] , p2 [ 2 ] , p3 [ 2 ] , p4 [ 2 ] ;
2014-04-27 00:22:49 +10:00
const float * v , * v1 , * v2 , * v3 , * v4 = NULL ;
2013-03-06 19:54:43 +00:00
int fidx ;
project_bucket_bounds ( ps , bucket_x , bucket_y , & bucket_bounds ) ;
2014-12-19 14:43:04 +01:00
2013-03-06 19:54:43 +00:00
/* Is one of the faces verts in the bucket bounds? */
fidx = mf - > v4 ? 3 : 2 ;
do {
v = ps - > screenCoords [ ( * ( & mf - > v1 + fidx ) ) ] ;
if ( BLI_rctf_isect_pt_v ( & bucket_bounds , v ) ) {
return 1 ;
}
} while ( fidx - - ) ;
v1 = ps - > screenCoords [ mf - > v1 ] ;
v2 = ps - > screenCoords [ mf - > v2 ] ;
v3 = ps - > screenCoords [ mf - > v3 ] ;
if ( mf - > v4 ) {
v4 = ps - > screenCoords [ mf - > v4 ] ;
}
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 ( mf - > v4 ) {
if ( isect_point_quad_v2 ( p1 , v1 , v2 , v3 , v4 ) | |
isect_point_quad_v2 ( p2 , v1 , v2 , v3 , v4 ) | |
isect_point_quad_v2 ( p3 , v1 , v2 , v3 , v4 ) | |
isect_point_quad_v2 ( p4 , v1 , v2 , v3 , v4 ) | |
/* we can avoid testing v3,v1 because another intersection MUST exist if this intersects */
( isect_line_line_v2 ( p1 , p2 , v1 , v2 ) | | isect_line_line_v2 ( p1 , p2 , v2 , v3 ) | | isect_line_line_v2 ( p1 , p2 , v3 , v4 ) ) | |
( isect_line_line_v2 ( p2 , p3 , v1 , v2 ) | | isect_line_line_v2 ( p2 , p3 , v2 , v3 ) | | isect_line_line_v2 ( p2 , p3 , v3 , v4 ) ) | |
( isect_line_line_v2 ( p3 , p4 , v1 , v2 ) | | isect_line_line_v2 ( p3 , p4 , v2 , v3 ) | | isect_line_line_v2 ( p3 , p4 , v3 , v4 ) ) | |
( isect_line_line_v2 ( p4 , p1 , v1 , v2 ) | | isect_line_line_v2 ( p4 , p1 , v2 , v3 ) | | isect_line_line_v2 ( p4 , p1 , v3 , v4 ) ) )
{
return 1 ;
}
}
else {
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_line_line_v2 ( p1 , p2 , v1 , v2 ) | | isect_line_line_v2 ( p1 , p2 , v2 , v3 ) ) | |
( isect_line_line_v2 ( p2 , p3 , v1 , v2 ) | | isect_line_line_v2 ( p2 , p3 , v2 , v3 ) ) | |
( isect_line_line_v2 ( p3 , p4 , v1 , v2 ) | | isect_line_line_v2 ( p3 , p4 , v2 , v3 ) ) | |
( isect_line_line_v2 ( p4 , p1 , v1 , v2 ) | | isect_line_line_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 MFace * mf , const int face_index )
{
float min [ 2 ] , max [ 2 ] , * vCoSS ;
int bucketMin [ 2 ] , bucketMax [ 2 ] ; /* for ps->bucketRect indexing */
int fidx , bucket_x , bucket_y ;
int has_x_isect = - 1 , has_isect = 0 ; /* for early loop exit */
MemArena * arena = ps - > arena_mt [ 0 ] ; /* just use the first thread arena since threading has not started yet */
INIT_MINMAX2 ( min , max ) ;
fidx = mf - > v4 ? 3 : 2 ;
do {
vCoSS = ps - > screenCoords [ * ( & mf - > v1 + 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 , mf ) ) {
int bucket_index = bucket_x + ( bucket_y * ps - > buckets_x ) ;
BLI_linklist_prepend_arena (
& ps - > bucketFaces [ bucket_index ] ,
SET_INT_IN_POINTER ( face_index ) , /* cast to a pointer to shut up the compiler */
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 ) {
if ( ! mf - > v4 ) {
ps - > faceSeamFlags [ face_index ] | = PROJ_FACE_NOSEAM4 ; /* so this wont show up as an untagged edge */
}
* * ps - > faceSeamUVs [ face_index ] = FLT_MAX ; /* set as uninitialized */
}
# endif
}
/* run once per stroke before projection painting */
static void project_paint_begin ( ProjPaintState * ps )
{
/* Viewport vars */
float mat [ 3 ] [ 3 ] ;
float no [ 3 ] ;
float * projScreenCo ; /* Note, we could have 4D vectors are only needed for */
float projMargin ;
/* Image Vars - keep track of images we have used */
LinkNode * image_LinkList = NULL ;
LinkNode * node ;
ProjPaintImage * projIma ;
Image * tpage_last = NULL , * tpage ;
2014-07-21 12:02:05 +02:00
TexPaintSlot * slot_last = NULL , * slot = NULL ;
TexPaintSlot * slot_last_clone = NULL , * slot_clone ;
2013-03-06 19:54:43 +00:00
/* Face vars */
2013-07-09 08:17:14 +00:00
MPoly * mpoly_orig ;
2013-03-06 19:54:43 +00:00
MFace * mf ;
2014-07-21 12:02:05 +02:00
MTFace * * tf ;
MTFace * tf_base ;
MTFace * * tf_clone ;
2014-09-05 10:58:44 +10:00
MTFace * tf_clone_base = NULL ;
2013-03-06 19:54:43 +00:00
int a , i ; /* generic looping vars */
int image_index = - 1 , face_index ;
2013-07-22 18:19:02 +00:00
/* double lookup */
const int * index_mf_to_mpoly = NULL ;
const int * index_mp_to_orig = NULL ;
2013-03-06 19:54:43 +00:00
MVert * mv ;
MemArena * arena ; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */
const int diameter = 2 * BKE_brush_size_get ( ps - > scene , ps - > brush ) ;
2013-07-08 15:35:53 +00:00
bool reset_threads = false ;
2013-03-06 19:54:43 +00:00
/* ---- end defines ---- */
if ( ps - > source = = PROJ_SRC_VIEW )
ED_view3d_clipping_local ( ps - > rv3d , ps - > ob - > obmat ) ; /* faster clipping lookups */
2013-07-09 08:17:14 +00:00
ps - > do_face_sel = ( ( ( ( Mesh * ) ps - > ob - > data ) - > editflag & ME_EDIT_PAINT_FACE_SEL ) ! = 0 ) ;
2013-03-06 19:54:43 +00:00
/* paint onto the derived mesh */
/* 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 - > dm = mesh_create_derived_render ( ps - > scene , ps - > ob , ps - > scene - > customdata_mask | CD_MASK_MTFACE ) ;
2014-04-01 11:34:00 +11:00
ps - > dm_release = true ;
2013-03-06 19:54:43 +00:00
}
2013-07-09 08:17:14 +00:00
else if ( ps - > ob - > derivedFinal & &
CustomData_has_layer ( & ps - > ob - > derivedFinal - > faceData , CD_MTFACE ) & &
( ps - > do_face_sel = = false | | CustomData_has_layer ( & ps - > ob - > derivedFinal - > polyData , CD_ORIGINDEX ) ) )
{
2013-03-06 19:54:43 +00:00
ps - > dm = ps - > ob - > derivedFinal ;
2014-04-01 11:34:00 +11:00
ps - > dm_release = false ;
2013-03-06 19:54:43 +00:00
}
else {
2013-07-09 08:17:14 +00:00
ps - > dm = mesh_get_derived_final (
ps - > scene , ps - > ob ,
ps - > scene - > customdata_mask | CD_MASK_MTFACE | ( ps - > do_face_sel ? CD_ORIGINDEX : 0 ) ) ;
2014-04-01 11:34:00 +11:00
ps - > dm_release = true ;
2013-03-06 19:54:43 +00:00
}
2014-08-27 09:49:31 +10:00
if ( ! CustomData_has_layer ( & ps - > dm - > faceData , CD_MTFACE ) ) {
2013-03-06 19:54:43 +00:00
if ( ps - > dm_release )
ps - > dm - > release ( ps - > dm ) ;
ps - > dm = NULL ;
return ;
}
2014-07-21 12:02:05 +02:00
DM_update_materials ( ps - > dm , ps - > ob ) ;
2013-03-06 19:54:43 +00:00
ps - > dm_totvert = ps - > dm - > getNumVerts ( ps - > dm ) ;
ps - > dm_totface = ps - > dm - > getNumTessFaces ( ps - > dm ) ;
2014-07-21 12:02:05 +02:00
ps - > dm_mvert = ps - > dm - > getVertArray ( ps - > dm ) ;
ps - > dm_mface = ps - > dm - > getTessFaceArray ( ps - > dm ) ;
ps - > dm_mtface = MEM_mallocN ( ps - > dm_totface * sizeof ( MTFace * ) , " proj_paint_mtfaces " ) ;
2013-07-09 08:17:14 +00:00
if ( ps - > do_face_sel ) {
2013-07-22 18:19:02 +00:00
index_mf_to_mpoly = ps - > dm - > getTessFaceDataArray ( ps - > dm , CD_ORIGINDEX ) ;
index_mp_to_orig = ps - > dm - > getPolyDataArray ( ps - > dm , CD_ORIGINDEX ) ;
if ( index_mf_to_mpoly = = NULL ) {
index_mp_to_orig = NULL ;
}
else {
mpoly_orig = ( ( Mesh * ) ps - > ob - > data ) - > mpoly ;
}
2013-07-09 08:17:14 +00:00
}
else {
mpoly_orig = NULL ;
}
2013-03-06 19:54:43 +00:00
/* use clone mtface? */
if ( ps - > do_layer_clone ) {
2014-07-21 12:02:05 +02:00
ps - > dm_mtface_clone = MEM_mallocN ( ps - > dm_totface * sizeof ( MTFace * ) , " proj_paint_mtfaces " ) ;
2013-03-06 19:54:43 +00:00
}
2014-07-21 12:02:05 +02:00
if ( ps - > do_layer_stencil | | ps - > do_stencil_brush ) {
2013-03-06 19:54:43 +00:00
//int layer_num = CustomData_get_stencil_layer(&ps->dm->faceData, CD_MTFACE);
2013-06-24 16:06:27 +00:00
int layer_num = CustomData_get_stencil_layer ( & ( ( Mesh * ) ps - > ob - > data ) - > pdata , CD_MTEXPOLY ) ;
2013-03-06 19:54:43 +00:00
if ( layer_num ! = - 1 )
ps - > dm_mtface_stencil = CustomData_get_layer_n ( & ps - > dm - > faceData , CD_MTFACE , layer_num ) ;
2014-07-21 12:02:05 +02:00
if ( ps - > dm_mtface_stencil = = NULL ) {
/* get active instead */
ps - > dm_mtface_stencil = CustomData_get_layer ( & ps - > dm - > faceData , CD_MTFACE ) ;
2013-03-06 19:54:43 +00:00
}
2014-07-21 12:02:05 +02:00
if ( ps - > do_stencil_brush )
tf_base = ps - > dm_mtface_stencil ;
2013-03-06 19:54:43 +00:00
}
2014-08-28 16:40:38 +02:00
if ( ps - > do_layer_clone ) {
int layer_num = CustomData_get_clone_layer ( & ( ( Mesh * ) ps - > ob - > data ) - > pdata , CD_MTEXPOLY ) ;
if ( layer_num ! = - 1 )
tf_clone_base = CustomData_get_layer_n ( & ps - > dm - > faceData , CD_MTFACE , layer_num ) ;
if ( tf_clone_base = = NULL ) {
/* get active instead */
tf_clone_base = CustomData_get_layer ( & ps - > dm - > faceData , CD_MTFACE ) ;
}
}
2013-03-06 19:54:43 +00:00
/* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */
if ( ps - > dm - > type ! = DM_TYPE_CDDM ) {
ps - > dm_mvert = MEM_dupallocN ( ps - > dm_mvert ) ;
ps - > dm_mface = MEM_dupallocN ( ps - > dm_mface ) ;
/* looks like these are ok for now.*/
#if 0
ps - > dm_mtface = MEM_dupallocN ( ps - > dm_mtface ) ;
ps - > dm_mtface_clone = MEM_dupallocN ( ps - > dm_mtface_clone ) ;
ps - > dm_mtface_stencil = MEM_dupallocN ( ps - > dm_mtface_stencil ) ;
# endif
}
ps - > viewDir [ 0 ] = 0.0f ;
ps - > viewDir [ 1 ] = 0.0f ;
ps - > viewDir [ 2 ] = 1.0f ;
{
float viewmat [ 4 ] [ 4 ] ;
float viewinv [ 4 ] [ 4 ] ;
invert_m4_m4 ( ps - > ob - > imat , ps - > ob - > obmat ) ;
2014-12-18 16:28:29 +01:00
if ( ELEM ( ps - > source , PROJ_SRC_VIEW , PROJ_SRC_VIEW_FILL ) ) {
2013-03-06 19:54:43 +00:00
/* 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 ( ps - > rv3d , ps - > ob , ps - > projectMat ) ;
2013-03-14 07:56:40 +00:00
ps - > is_ortho = ED_view3d_clip_range_get ( ps - > v3d , ps - > rv3d , & ps - > clipsta , & ps - > clipend , true ) ;
2013-03-06 19:54:43 +00:00
}
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 ) ;
2014-04-27 00:22:49 +10:00
const float * array = ( float * ) IDP_Array ( view_data ) ;
2013-03-06 19:54:43 +00:00
/* 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 - > clipsta = array [ 0 ] ;
ps - > clipend = 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 = ps - > scene - > camera ;
CameraParams params ;
/* viewmat & viewinv */
copy_m4_m4 ( viewinv , cam_ob - > obmat ) ;
normalize_m4 ( viewinv ) ;
invert_m4_m4 ( viewmat , viewinv ) ;
/* window matrix, clipping and ortho */
BKE_camera_params_init ( & params ) ;
BKE_camera_params_from_object ( & params , cam_ob ) ;
BKE_camera_params_compute_viewplane ( & params , ps - > winx , ps - > winy , 1.0f , 1.0f ) ;
BKE_camera_params_compute_matrix ( & params ) ;
copy_m4_m4 ( winmat , params . winmat ) ;
ps - > clipsta = params . clipsta ;
ps - > clipend = params . clipend ;
ps - > is_ortho = params . is_ortho ;
}
/* same as #ED_view3d_ob_project_mat_get */
2013-05-26 18:36:25 +00:00
mul_m4_m4m4 ( vmat , viewmat , ps - > ob - > obmat ) ;
mul_m4_m4m4 ( ps - > projectMat , winmat , vmat ) ;
2013-03-06 19:54:43 +00:00
}
/* viewDir - object relative */
invert_m4_m4 ( ps - > ob - > imat , ps - > ob - > obmat ) ;
copy_m3_m4 ( mat , viewinv ) ;
mul_m3_v3 ( mat , ps - > viewDir ) ;
copy_m3_m4 ( mat , ps - > ob - > imat ) ;
mul_m3_v3 ( mat , ps - > viewDir ) ;
normalize_v3 ( ps - > viewDir ) ;
/* viewPos - object relative */
copy_v3_v3 ( ps - > viewPos , viewinv [ 3 ] ) ;
copy_m3_m4 ( mat , ps - > ob - > imat ) ;
mul_m3_v3 ( mat , ps - > viewPos ) ;
add_v3_v3 ( ps - > viewPos , ps - > ob - > imat [ 3 ] ) ;
}
/* calculate vert screen coords
* run this early so we can calculate the x / y resolution of our bucket rect */
INIT_MINMAX2 ( ps - > screenMin , ps - > screenMax ) ;
ps - > screenCoords = MEM_mallocN ( sizeof ( float ) * ps - > dm_totvert * 4 , " ProjectPaint ScreenVerts " ) ;
projScreenCo = * ps - > screenCoords ;
if ( ps - > is_ortho ) {
for ( a = 0 , mv = ps - > dm_mvert ; a < ps - > dm_totvert ; a + + , mv + + , projScreenCo + = 4 ) {
mul_v3_m4v3 ( projScreenCo , ps - > projectMat , mv - > co ) ;
/* screen space, not clamped */
2013-04-28 11:55:41 +00:00
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 ] ;
2013-03-06 19:54:43 +00:00
minmax_v2v2_v2 ( ps - > screenMin , ps - > screenMax , projScreenCo ) ;
}
}
else {
for ( a = 0 , mv = ps - > dm_mvert ; a < ps - > dm_totvert ; a + + , mv + + , projScreenCo + = 4 ) {
copy_v3_v3 ( projScreenCo , mv - > co ) ;
projScreenCo [ 3 ] = 1.0f ;
mul_m4_v4 ( ps - > projectMat , projScreenCo ) ;
if ( projScreenCo [ 3 ] > ps - > clipsta ) {
/* screen space, not clamped */
2013-04-28 11:55:41 +00:00
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 ] ;
2013-03-06 19:54:43 +00:00
projScreenCo [ 2 ] = projScreenCo [ 2 ] / projScreenCo [ 3 ] ; /* Use the depth for bucket point occlusion */
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
2014-10-29 14:11:19 +01:00
* have a parallel edge and at the bounds of the 2 D projected verts eg
2013-03-06 19:54:43 +00:00
* - 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 [ 1 ] , ( float ) ( - diameter ) , ( float ) ( ps - > winy + diameter ) ) ;
CLAMP ( ps - > screenMax [ 1 ] , ( float ) ( - diameter ) , ( float ) ( ps - > winy + diameter ) ) ;
# endif
}
2014-12-18 16:28:29 +01:00
else if ( ps - > source ! = PROJ_SRC_VIEW_FILL ) { /* re-projection, use bounds */
2013-03-06 19:54:43 +00:00
ps - > screenMin [ 0 ] = 0 ;
ps - > screenMax [ 0 ] = ( float ) ( ps - > winx ) ;
ps - > screenMin [ 1 ] = 0 ;
ps - > screenMax [ 1 ] = ( float ) ( ps - > winy ) ;
}
/* 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 ) ) ;
/* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */
2013-07-08 15:35:53 +00:00
if ( ps - > buckets_x > PROJ_BUCKET_RECT_MAX | | ps - > buckets_y > PROJ_BUCKET_RECT_MAX ) {
reset_threads = true ;
}
2013-03-06 19:54:43 +00:00
/* 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 = ( LinkNode * * ) MEM_callocN ( sizeof ( LinkNode * ) * ps - > buckets_x * ps - > buckets_y , " paint-bucketRect " ) ;
ps - > bucketFaces = ( LinkNode * * ) MEM_callocN ( sizeof ( LinkNode * ) * ps - > buckets_x * ps - > buckets_y , " paint-bucketFaces " ) ;
ps - > bucketFlags = ( unsigned char * ) MEM_callocN ( sizeof ( char ) * ps - > buckets_x * ps - > buckets_y , " paint-bucketFaces " ) ;
# ifndef PROJ_DEBUG_NOSEAMBLEED
if ( ps - > seam_bleed_px > 0.0f ) {
ps - > vertFaces = ( LinkNode * * ) MEM_callocN ( sizeof ( LinkNode * ) * ps - > dm_totvert , " paint-vertFaces " ) ;
ps - > faceSeamFlags = ( char * ) MEM_callocN ( sizeof ( char ) * ps - > dm_totface , " paint-faceSeamFlags " ) ;
2014-05-21 18:35:08 +02:00
ps - > faceWindingFlags = ( char * ) MEM_callocN ( sizeof ( char ) * ps - > dm_totface , " paint-faceWindindFlags " ) ;
2013-03-06 19:54:43 +00:00
ps - > faceSeamUVs = MEM_mallocN ( sizeof ( float ) * ps - > dm_totface * 8 , " paint-faceSeamUVs " ) ;
}
# endif
/* 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 . */
2013-05-08 13:23:17 +00:00
ps - > thread_tot = BKE_scene_num_threads ( ps - > scene ) ;
2013-07-08 15:35:53 +00:00
/* workaround for #35057, disable threading if diameter is less than is possible for
* optimum bucket number generation */
if ( reset_threads )
ps - > thread_tot = 1 ;
2014-07-21 12:02:05 +02:00
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 ( ) ;
2013-03-06 19:54:43 +00:00
for ( a = 0 ; a < ps - > thread_tot ; a + + ) {
2013-10-10 18:18:13 +00:00
ps - > arena_mt [ a ] = BLI_memarena_new ( MEM_SIZE_OPTIMAL ( 1 < < 16 ) , " project paint arena " ) ;
2013-03-06 19:54:43 +00:00
}
arena = ps - > arena_mt [ 0 ] ;
if ( ps - > do_backfacecull & & ps - > do_mask_normal ) {
float viewDirPersp [ 3 ] ;
ps - > vertFlags = MEM_callocN ( sizeof ( char ) * ps - > dm_totvert , " paint-vertFlags " ) ;
for ( a = 0 , mv = ps - > dm_mvert ; a < ps - > dm_totvert ; a + + , mv + + ) {
normal_short_to_float_v3 ( no , mv - > no ) ;
if ( ps - > is_ortho ) {
if ( angle_normalized_v3v3 ( ps - > viewDir , no ) > = ps - > normal_angle ) { /* 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 ( angle_normalized_v3v3 ( viewDirPersp , no ) > = ps - > normal_angle ) { /* 1 vert of this face is towards us */
ps - > vertFlags [ a ] | = PROJ_VERT_CULL ;
}
}
}
}
for ( face_index = 0 , tf = ps - > dm_mtface , mf = ps - > dm_mface ; face_index < ps - > dm_totface ; mf + + , tf + + , face_index + + ) {
2013-07-09 08:17:14 +00:00
bool is_face_sel ;
2013-03-06 19:54:43 +00:00
# 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 ] , SET_INT_IN_POINTER ( face_index ) , arena ) ;
BLI_linklist_prepend_arena ( & ps - > vertFaces [ mf - > v2 ] , SET_INT_IN_POINTER ( face_index ) , arena ) ;
BLI_linklist_prepend_arena ( & ps - > vertFaces [ mf - > v3 ] , SET_INT_IN_POINTER ( face_index ) , arena ) ;
if ( mf - > v4 ) {
BLI_linklist_prepend_arena ( & ps - > vertFaces [ mf - > v4 ] , SET_INT_IN_POINTER ( face_index ) , arena ) ;
}
}
# endif
2013-07-09 08:17:14 +00:00
if ( ps - > do_face_sel ) {
int orig_index ;
2013-07-22 18:19:02 +00:00
if ( index_mp_to_orig & & ( ( orig_index = DM_origindex_mface_mpoly ( index_mf_to_mpoly , index_mp_to_orig ,
face_index ) ) ) ! = ORIGINDEX_NONE )
{
MPoly * mp = & mpoly_orig [ orig_index ] ;
2013-07-09 08:17:14 +00:00
is_face_sel = ( ( mp - > flag & ME_FACE_SEL ) ! = 0 ) ;
}
else {
is_face_sel = ( ( mf - > flag & ME_FACE_SEL ) ! = 0 ) ;
}
}
else {
is_face_sel = true ;
}
2013-03-06 19:54:43 +00:00
2014-07-21 12:02:05 +02:00
if ( ! ps - > do_stencil_brush ) {
slot = project_paint_face_paint_slot ( ps , face_index ) ;
/* all faces should have a valid slot, reassert here */
2014-07-31 11:46:19 +02:00
if ( slot = = NULL ) {
tf_base = CustomData_get_layer ( & ps - > dm - > faceData , CD_MTFACE ) ;
2014-08-28 16:40:38 +02:00
tpage = ps - > canvas_ima ;
2014-07-21 12:02:05 +02:00
}
2014-07-31 11:46:19 +02:00
else {
if ( slot ! = slot_last ) {
if ( ! slot - > uvname | | ! ( tf_base = CustomData_get_layer_named ( & ps - > dm - > faceData , CD_MTFACE , slot - > uvname ) ) )
tf_base = CustomData_get_layer ( & ps - > dm - > faceData , CD_MTFACE ) ;
slot_last = slot ;
}
2014-07-21 12:02:05 +02:00
2014-07-31 11:46:19 +02:00
/* don't allow using the same inage for painting and stencilling */
if ( slot - > ima = = ps - > stencil_ima )
continue ;
2014-08-28 16:40:38 +02:00
tpage = slot - > ima ;
2014-07-31 11:46:19 +02:00
}
2014-07-21 12:02:05 +02:00
}
2014-08-28 16:40:38 +02:00
else {
tpage = ps - > stencil_ima ;
}
2014-07-21 12:02:05 +02:00
* tf = tf_base + face_index ;
if ( ps - > do_layer_clone ) {
2014-08-28 16:40:38 +02:00
if ( ps - > do_material_slots ) {
slot_clone = project_paint_face_clone_slot ( ps , face_index ) ;
/* all faces should have a valid slot, reassert here */
if ( ELEM ( slot_clone , NULL , slot ) )
continue ;
}
else if ( ps - > clone_ima = = ps - > canvas_ima )
2014-07-21 12:02:05 +02:00
continue ;
2014-08-28 16:40:38 +02:00
2014-07-21 12:02:05 +02:00
tf_clone = ps - > dm_mtface_clone + face_index ;
2014-08-28 16:40:38 +02:00
if ( ps - > do_material_slots ) {
if ( slot_clone ! = slot_last_clone ) {
if ( ! slot - > uvname | | ! ( tf_clone_base = CustomData_get_layer_named ( & ps - > dm - > faceData , CD_MTFACE , slot_clone - > uvname ) ) )
tf_clone_base = CustomData_get_layer ( & ps - > dm - > faceData , CD_MTFACE ) ;
slot_last_clone = slot_clone ;
}
2014-07-21 12:02:05 +02:00
}
2014-08-28 16:40:38 +02:00
2014-07-21 12:02:05 +02:00
* tf_clone = tf_clone_base + face_index ;
}
/* tfbase here should be non-null! */
BLI_assert ( tf_base ! = NULL ) ;
2014-08-28 16:40:38 +02:00
if ( is_face_sel & & tpage ) {
2014-04-27 00:22:49 +10:00
const float * v1coSS , * v2coSS , * v3coSS , * v4coSS = NULL ;
2013-03-06 19:54:43 +00:00
v1coSS = ps - > screenCoords [ mf - > v1 ] ;
v2coSS = ps - > screenCoords [ mf - > v2 ] ;
v3coSS = ps - > screenCoords [ mf - > v3 ] ;
if ( mf - > v4 ) {
v4coSS = ps - > screenCoords [ mf - > v4 ] ;
}
if ( ! ps - > is_ortho ) {
if ( v1coSS [ 0 ] = = FLT_MAX | |
v2coSS [ 0 ] = = FLT_MAX | |
v3coSS [ 0 ] = = FLT_MAX | |
( mf - > v4 & & v4coSS [ 0 ] = = FLT_MAX ) )
{
continue ;
}
}
# ifdef PROJ_DEBUG_WINCLIP
/* ignore faces outside the view */
2014-12-18 16:28:29 +01:00
if ( ( ps - > source ! = PROJ_SRC_VIEW_FILL ) & &
( ( v1coSS [ 0 ] < ps - > screenMin [ 0 ] & &
2013-03-06 19:54:43 +00:00
v2coSS [ 0 ] < ps - > screenMin [ 0 ] & &
v3coSS [ 0 ] < ps - > screenMin [ 0 ] & &
( mf - > v4 & & v4coSS [ 0 ] < ps - > screenMin [ 0 ] ) ) | |
( v1coSS [ 0 ] > ps - > screenMax [ 0 ] & &
v2coSS [ 0 ] > ps - > screenMax [ 0 ] & &
v3coSS [ 0 ] > ps - > screenMax [ 0 ] & &
( mf - > v4 & & v4coSS [ 0 ] > ps - > screenMax [ 0 ] ) ) | |
( v1coSS [ 1 ] < ps - > screenMin [ 1 ] & &
v2coSS [ 1 ] < ps - > screenMin [ 1 ] & &
v3coSS [ 1 ] < ps - > screenMin [ 1 ] & &
( mf - > v4 & & v4coSS [ 1 ] < ps - > screenMin [ 1 ] ) ) | |
( v1coSS [ 1 ] > ps - > screenMax [ 1 ] & &
v2coSS [ 1 ] > ps - > screenMax [ 1 ] & &
v3coSS [ 1 ] > ps - > screenMax [ 1 ] & &
2014-12-18 16:28:29 +01:00
( mf - > v4 & & v4coSS [ 1 ] > ps - > screenMax [ 1 ] ) ) )
2013-03-06 19:54:43 +00:00
)
{
continue ;
}
# endif //PROJ_DEBUG_WINCLIP
if ( ps - > do_backfacecull ) {
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 {
if ( line_point_side_v2 ( v1coSS , v2coSS , v3coSS ) < 0.0f ) {
continue ;
}
}
}
if ( tpage_last ! = tpage ) {
image_index = BLI_linklist_index ( image_LinkList , 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 , mf , face_index ) ;
}
}
}
/* build an array of images we use*/
projIma = ps - > projImages = ( ProjPaintImage * ) BLI_memarena_alloc ( arena , sizeof ( ProjPaintImage ) * ps - > image_tot ) ;
for ( node = image_LinkList , i = 0 ; node ; node = node - > next , i + + , projIma + + ) {
2014-07-21 12:02:05 +02:00
int size ;
2013-03-06 19:54:43 +00:00
projIma - > ima = node - > link ;
projIma - > touch = 0 ;
projIma - > ibuf = BKE_image_acquire_ibuf ( projIma - > ima , NULL , NULL ) ;
2014-07-21 12:02:05 +02:00
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 ) ;
memset ( projIma - > partRedrawRect , 0 , sizeof ( ImagePaintPartialRedraw ) * PROJ_BOUNDBOX_SQUARED ) ;
projIma - > undoRect = ( volatile void * * ) BLI_memarena_alloc ( arena , size ) ;
memset ( projIma - > undoRect , 0 , size ) ;
projIma - > maskRect = ( unsigned short * * ) BLI_memarena_alloc ( arena , size ) ;
memset ( projIma - > maskRect , 0 , size ) ;
projIma - > valid = ( bool * * ) BLI_memarena_alloc ( arena , size ) ;
memset ( projIma - > valid , 0 , size ) ;
2013-03-06 19:54:43 +00:00
}
/* we have built the array, discard the linked list */
BLI_linklist_free ( image_LinkList , NULL ) ;
}
2013-05-15 11:16:01 +00:00
static void paint_proj_begin_clone ( ProjPaintState * ps , const float mouse [ 2 ] )
2013-03-06 19:54:43 +00:00
{
/* setup clone offset */
if ( ps - > tool = = PAINT_TOOL_CLONE ) {
float projCo [ 4 ] ;
2013-10-26 04:07:18 +00:00
copy_v3_v3 ( projCo , ED_view3d_cursor3d_get ( ps - > scene , ps - > v3d ) ) ;
2013-03-06 19:54:43 +00:00
mul_m4_v3 ( ps - > ob - > imat , projCo ) ;
projCo [ 3 ] = 1.0f ;
mul_m4_v4 ( ps - > projectMat , projCo ) ;
2013-04-28 11:55:41 +00:00
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 ] ) ;
2013-03-06 19:54:43 +00:00
}
}
static void project_paint_end ( ProjPaintState * ps )
{
int a ;
ProjPaintImage * projIma ;
2014-07-21 12:02:05 +02:00
image_undo_remove_masks ( ) ;
2013-03-06 19:54:43 +00:00
/* dereference used image buffers */
for ( a = 0 , projIma = ps - > projImages ; a < ps - > image_tot ; a + + , projIma + + ) {
BKE_image_release_ibuf ( projIma - > ima , projIma - > ibuf , NULL ) ;
2014-07-21 12:02:05 +02:00
DAG_id_tag_update ( & projIma - > ima - > id , 0 ) ;
2013-03-06 19:54:43 +00:00
}
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 ) ;
2014-07-21 12:02:05 +02:00
MEM_freeN ( ps - > dm_mtface ) ;
if ( ps - > do_layer_clone )
MEM_freeN ( ps - > dm_mtface_clone ) ;
if ( ps - > thread_tot > 1 ) {
BLI_spin_end ( ps - > tile_lock ) ;
MEM_freeN ( ( void * ) ps - > tile_lock ) ;
}
image_undo_end_locks ( ) ;
2013-03-06 19:54:43 +00:00
# ifndef PROJ_DEBUG_NOSEAMBLEED
if ( ps - > seam_bleed_px > 0.0f ) {
MEM_freeN ( ps - > vertFaces ) ;
MEM_freeN ( ps - > faceSeamFlags ) ;
2014-05-21 18:35:08 +02:00
MEM_freeN ( ps - > faceWindingFlags ) ;
2013-03-06 19:54:43 +00:00
MEM_freeN ( ps - > faceSeamUVs ) ;
}
# endif
2014-07-21 12:02:05 +02:00
if ( ps - > blurkernel ) {
paint_delete_blur_kernel ( ps - > blurkernel ) ;
MEM_freeN ( ps - > blurkernel ) ;
}
2013-03-06 19:54:43 +00:00
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 */
if ( ps - > dm - > type ! = DM_TYPE_CDDM ) {
if ( ps - > dm_mvert ) MEM_freeN ( ps - > dm_mvert ) ;
if ( ps - > dm_mface ) MEM_freeN ( ps - > dm_mface ) ;
/* looks like these don't need copying */
#if 0
if ( ps - > dm_mtface ) MEM_freeN ( ps - > dm_mtface ) ;
if ( ps - > dm_mtface_clone ) MEM_freeN ( ps - > dm_mtface_clone ) ;
if ( ps - > dm_mtface_stencil ) MEM_freeN ( ps - > dm_mtface_stencil ) ;
# endif
}
if ( ps - > dm_release )
ps - > dm - > release ( ps - > dm ) ;
}
/* 1 = an undo, -1 is a redo. */
static void partial_redraw_array_init ( ImagePaintPartialRedraw * pr )
{
int tot = PROJ_BOUNDBOX_SQUARED ;
while ( tot - - ) {
pr - > x1 = 10000000 ;
pr - > y1 = 10000000 ;
pr - > x2 = - 1 ;
pr - > y2 = - 1 ;
pr - > enabled = 1 ;
pr + + ;
}
}
2013-10-15 07:35:12 +00:00
static bool partial_redraw_array_merge ( ImagePaintPartialRedraw * pr , ImagePaintPartialRedraw * pr_other , int tot )
2013-03-06 19:54:43 +00:00
{
2013-10-15 07:35:12 +00:00
bool touch = 0 ;
2013-03-06 19:54:43 +00:00
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 ) ;
if ( pr - > x2 ! = - 1 )
touch = 1 ;
pr + + ; pr_other + + ;
}
return touch ;
}
/* Loop over all images on this mesh and update any we have touched */
2013-10-15 07:35:12 +00:00
static bool project_image_refresh_tagged ( ProjPaintState * ps )
2013-03-06 19:54:43 +00:00
{
ImagePaintPartialRedraw * pr ;
ProjPaintImage * projIma ;
int a , i ;
2013-10-15 07:35:12 +00:00
bool redraw = false ;
2013-03-06 19:54:43 +00:00
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' ? */
2013-03-07 12:11:38 +00:00
set_imapaintpartial ( pr ) ;
2013-03-06 19:54:43 +00:00
imapaint_image_update ( NULL , projIma - > ima , projIma - > ibuf , true ) ;
redraw = 1 ;
}
}
projIma - > touch = 0 ; /* clear for reuse */
}
}
return redraw ;
}
/* run this per painting onto each mouse location */
2013-10-15 07:35:12 +00:00
static bool project_bucket_iter_init ( ProjPaintState * ps , const float mval_f [ 2 ] )
2013-03-06 19:54:43 +00:00
{
if ( ps - > source = = PROJ_SRC_VIEW ) {
float min_brush [ 2 ] , max_brush [ 2 ] ;
2014-07-21 12:02:05 +02:00
const float radius = ps - > brush_size ;
2013-03-06 19:54:43 +00:00
/* 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 ;
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 ) ;
/* 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 - > bucketMax [ 0 ] = ps - > buckets_x ;
ps - > bucketMax [ 1 ] = ps - > buckets_y ;
ps - > context_bucket_x = 0 ;
ps - > context_bucket_y = 0 ;
}
return 1 ;
}
2013-10-15 07:35:12 +00:00
static bool project_bucket_iter_next ( ProjPaintState * ps , int * bucket_index , rctf * bucket_bounds , const float mval [ 2 ] )
2013-03-06 19:54:43 +00:00
{
2014-07-21 12:02:05 +02:00
const int diameter = 2 * ps - > brush_size ;
2013-03-06 19:54:43 +00:00
if ( ps - > thread_tot > 1 )
BLI_lock_thread ( LOCK_CUSTOM1 ) ;
//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 + + ) {
/* 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 - > thread_tot > 1 )
BLI_unlock_thread ( LOCK_CUSTOM1 ) ;
return 1 ;
}
}
ps - > context_bucket_x = ps - > bucketMin [ 0 ] ;
}
if ( ps - > thread_tot > 1 )
BLI_unlock_thread ( 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 ] ;
/* annoying but we need to have image bounds per thread, then merge into ps->projectPartialRedraws */
ProjPaintImage * projImages ; /* array of partial redraws */
/* thread settings */
int thread_index ;
struct ImagePool * pool ;
} ProjectHandle ;
2013-04-28 11:55:41 +00:00
static void do_projectpaint_clone ( ProjPaintState * ps , ProjPixel * projPixel , float mask )
2013-03-06 19:54:43 +00:00
{
2013-04-28 11:55:41 +00:00
const unsigned char * clone_pt = ( ( ProjPixelClone * ) projPixel ) - > clonepx . ch ;
2013-03-06 19:54:43 +00:00
2013-04-28 11:55:41 +00:00
if ( clone_pt [ 3 ] ) {
unsigned char clone_rgba [ 4 ] ;
2013-03-06 19:54:43 +00:00
2013-04-28 11:55:41 +00:00
clone_rgba [ 0 ] = clone_pt [ 0 ] ;
clone_rgba [ 1 ] = clone_pt [ 1 ] ;
clone_rgba [ 2 ] = clone_pt [ 2 ] ;
2013-06-13 17:11:09 +00:00
clone_rgba [ 3 ] = ( unsigned char ) ( clone_pt [ 3 ] * mask ) ;
2013-03-06 19:54:43 +00:00
2013-04-28 11:55:41 +00:00
if ( ps - > do_masking ) {
2014-07-21 12:02:05 +02:00
IMB_blend_color_byte ( projPixel - > pixel . ch_pt , projPixel - > origColor . ch_pt , clone_rgba , ps - > blend ) ;
2013-04-28 11:55:41 +00:00
}
else {
IMB_blend_color_byte ( projPixel - > pixel . ch_pt , projPixel - > pixel . ch_pt , clone_rgba , ps - > blend ) ;
}
2013-03-06 19:54:43 +00:00
}
}
2013-04-28 11:55:41 +00:00
static void do_projectpaint_clone_f ( ProjPaintState * ps , ProjPixel * projPixel , float mask )
2013-03-06 19:54:43 +00:00
{
2013-04-28 11:55:41 +00:00
const float * clone_pt = ( ( ProjPixelClone * ) projPixel ) - > clonepx . f ;
if ( clone_pt [ 3 ] ) {
float clone_rgba [ 4 ] ;
mul_v4_v4fl ( clone_rgba , clone_pt , mask ) ;
if ( ps - > do_masking ) {
2014-07-21 12:02:05 +02:00
IMB_blend_color_float ( projPixel - > pixel . f_pt , projPixel - > origColor . f_pt , clone_rgba , ps - > blend ) ;
2013-04-28 11:55:41 +00:00
}
else {
IMB_blend_color_float ( projPixel - > pixel . f_pt , projPixel - > pixel . f_pt , clone_rgba , ps - > blend ) ;
}
2013-03-06 19:54:43 +00:00
}
}
2014-11-21 14:14:50 +01:00
/**
* \ note mask is used to modify the alpha here , this is not correct since it allows
* accumulation of color greater than ' projPixel - > mask ' however in the case of smear its not
2013-03-06 19:54:43 +00:00
* really that important to be correct as it is with clone and painting
*/
2013-04-28 11:55:41 +00:00
static void do_projectpaint_smear ( ProjPaintState * ps , ProjPixel * projPixel , float mask ,
2013-03-11 16:23:33 +00:00
MemArena * smearArena , LinkNode * * smearPixels , const float co [ 2 ] )
2013-03-06 19:54:43 +00:00
{
unsigned char rgba_ub [ 4 ] ;
if ( project_paint_PickColor ( ps , co , NULL , rgba_ub , 1 ) = = 0 )
return ;
2013-04-28 11:55:41 +00:00
2013-04-30 16:07:52 +00:00
blend_color_interpolate_byte ( ( ( ProjPixelClone * ) projPixel ) - > clonepx . ch , projPixel - > pixel . ch_pt , rgba_ub , mask ) ;
2013-03-06 19:54:43 +00:00
BLI_linklist_prepend_arena ( smearPixels , ( void * ) projPixel , smearArena ) ;
}
2013-04-28 11:55:41 +00:00
static void do_projectpaint_smear_f ( ProjPaintState * ps , ProjPixel * projPixel , float mask ,
2013-03-11 16:23:33 +00:00
MemArena * smearArena , LinkNode * * smearPixels_f , const float co [ 2 ] )
2013-03-06 19:54:43 +00:00
{
float rgba [ 4 ] ;
if ( project_paint_PickColor ( ps , co , rgba , NULL , 1 ) = = 0 )
return ;
2013-04-30 16:07:52 +00:00
blend_color_interpolate_float ( ( ( ProjPixelClone * ) projPixel ) - > clonepx . f , projPixel - > pixel . f_pt , rgba , mask ) ;
2013-03-06 19:54:43 +00:00
BLI_linklist_prepend_arena ( smearPixels_f , ( void * ) projPixel , smearArena ) ;
}
2013-04-28 11:55:41 +00:00
static void do_projectpaint_soften_f ( ProjPaintState * ps , ProjPixel * projPixel , float mask ,
2013-03-11 16:23:33 +00:00
MemArena * softenArena , LinkNode * * softenPixels )
2013-03-06 19:54:43 +00:00
{
2014-08-29 17:18:06 +02:00
float accum_tot = 0.0f ;
2014-07-21 12:02:05 +02:00
int xk , yk ;
BlurKernel * kernel = ps - > blurkernel ;
2013-03-06 19:54:43 +00:00
float * rgba = projPixel - > newColor . f ;
/* rather then painting, accumulate surrounding colors */
zero_v4 ( rgba ) ;
2014-07-21 12:02:05 +02:00
for ( yk = 0 ; yk < kernel - > side ; yk + + ) {
for ( xk = 0 ; xk < kernel - > side ; xk + + ) {
float rgba_tmp [ 4 ] ;
2014-08-27 15:07:57 +02:00
float co_ofs [ 2 ] = { 2.0f * xk - 1.0f , 2.0f * yk - 1.0f } ;
2014-07-21 12:02:05 +02:00
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 ;
}
2013-03-06 19:54:43 +00:00
}
}
if ( LIKELY ( accum_tot ! = 0 ) ) {
mul_v4_fl ( rgba , 1.0f / ( float ) accum_tot ) ;
2014-07-21 12:02:05 +02:00
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
2014-08-02 16:53:52 +10:00
* colored speckles appearing in final image , and also to check for threshold */
2014-07-21 12:02:05 +02:00
rgba [ 0 ] = rgba [ 1 ] = rgba [ 2 ] = rgb_to_grayscale ( 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 ) ;
2014-08-29 17:18:06 +02:00
rgba [ 3 ] = alpha ;
2014-07-21 12:02:05 +02:00
}
else
return ;
}
else {
blend_color_interpolate_float ( rgba , rgba , projPixel - > pixel . f_pt , mask ) ;
}
2013-03-06 19:54:43 +00:00
BLI_linklist_prepend_arena ( softenPixels , ( void * ) projPixel , softenArena ) ;
}
}
2013-04-28 11:55:41 +00:00
static void do_projectpaint_soften ( ProjPaintState * ps , ProjPixel * projPixel , float mask ,
2013-03-11 16:23:33 +00:00
MemArena * softenArena , LinkNode * * softenPixels )
2013-03-06 19:54:43 +00:00
{
2014-07-21 12:02:05 +02:00
float accum_tot = 0 ;
int xk , yk ;
BlurKernel * kernel = ps - > blurkernel ;
2013-03-06 19:54:43 +00:00
float rgba [ 4 ] ; /* convert to byte after */
/* rather then painting, accumulate surrounding colors */
zero_v4 ( rgba ) ;
2014-07-21 12:02:05 +02:00
for ( yk = 0 ; yk < kernel - > side ; yk + + ) {
for ( xk = 0 ; xk < kernel - > side ; xk + + ) {
float rgba_tmp [ 4 ] ;
2014-08-27 15:07:57 +02:00
float co_ofs [ 2 ] = { 2.0f * xk - 1.0f , 2.0f * yk - 1.0f } ;
2014-07-21 12:02:05 +02:00
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 ;
}
2013-03-06 19:54:43 +00:00
}
}
if ( LIKELY ( accum_tot ! = 0 ) ) {
unsigned char * rgba_ub = projPixel - > newColor . ch ;
mul_v4_fl ( rgba , 1.0f / ( float ) accum_tot ) ;
2014-07-21 12:02:05 +02:00
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
2014-08-02 16:53:52 +10:00
* colored speckles appearing in final image , and also to check for threshold */
2014-07-21 12:02:05 +02:00
rgba [ 0 ] = rgba [ 1 ] = rgba [ 2 ] = rgb_to_grayscale ( 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 , rgba_ub , projPixel - > pixel . ch_pt , mask ) ;
}
2013-03-06 19:54:43 +00:00
BLI_linklist_prepend_arena ( softenPixels , ( void * ) projPixel , softenArena ) ;
}
}
2013-04-28 11:55:41 +00:00
static void do_projectpaint_draw ( ProjPaintState * ps , ProjPixel * projPixel , const float texrgb [ 3 ] , float mask )
2013-03-06 19:54:43 +00:00
{
2013-04-28 11:55:41 +00:00
float rgb [ 3 ] ;
2013-03-06 19:54:43 +00:00
unsigned char rgba_ub [ 4 ] ;
2014-07-21 12:02:05 +02:00
copy_v3_v3 ( rgb , ps - > paint_color ) ;
2013-04-28 11:55:41 +00:00
2013-03-06 19:54:43 +00:00
if ( ps - > is_texbrush ) {
2013-04-28 11:55:41 +00:00
mul_v3_v3 ( rgb , texrgb ) ;
2013-12-03 22:13:15 +06:00
/* TODO(sergey): Support texture paint color space. */
linearrgb_to_srgb_v3_v3 ( rgb , rgb ) ;
2013-03-06 19:54:43 +00:00
}
2013-04-28 11:55:41 +00:00
rgb_float_to_uchar ( rgba_ub , rgb ) ;
2014-03-30 19:37:43 +11:00
rgba_ub [ 3 ] = f_to_char ( mask ) ;
2013-04-28 11:55:41 +00:00
if ( ps - > do_masking ) {
2014-07-21 12:02:05 +02:00
IMB_blend_color_byte ( projPixel - > pixel . ch_pt , projPixel - > origColor . ch_pt , rgba_ub , ps - > blend ) ;
2013-03-06 19:54:43 +00:00
}
else {
2013-04-28 11:55:41 +00:00
IMB_blend_color_byte ( projPixel - > pixel . ch_pt , projPixel - > pixel . ch_pt , rgba_ub , ps - > blend ) ;
2013-03-06 19:54:43 +00:00
}
}
2013-04-28 11:55:41 +00:00
static void do_projectpaint_draw_f ( ProjPaintState * ps , ProjPixel * projPixel , const float texrgb [ 3 ] , float mask )
2013-03-06 19:54:43 +00:00
{
2013-04-28 11:55:41 +00:00
float rgba [ 4 ] ;
2014-07-21 12:02:05 +02:00
copy_v3_v3 ( rgba , ps - > paint_color_linear ) ;
2013-04-28 11:55:41 +00:00
if ( ps - > is_texbrush )
mul_v3_v3 ( rgba , texrgb ) ;
mul_v3_fl ( rgba , mask ) ;
rgba [ 3 ] = mask ;
2013-03-06 19:54:43 +00:00
2013-04-28 11:55:41 +00:00
if ( ps - > do_masking ) {
2014-07-21 12:02:05 +02:00
IMB_blend_color_float ( projPixel - > pixel . f_pt , projPixel - > origColor . f_pt , rgba , ps - > blend ) ;
2013-03-06 19:54:43 +00:00
}
else {
2013-04-28 11:55:41 +00:00
IMB_blend_color_float ( projPixel - > pixel . f_pt , projPixel - > pixel . f_pt , rgba , ps - > blend ) ;
2013-03-06 19:54:43 +00:00
}
}
2014-07-21 12:02:05 +02:00
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 ) ;
}
}
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 ) ;
}
}
2013-03-06 19:54:43 +00:00
/* 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 ;
2014-02-03 02:46:45 +11:00
float dist_sq , dist ;
2013-03-06 19:54:43 +00:00
float falloff ;
int bucket_index ;
2013-10-15 07:18:23 +00:00
bool is_floatbuf = false ;
2013-03-06 19:54:43 +00:00
const short tool = ps - > tool ;
rctf bucket_bounds ;
/* for smear only */
float pos_ofs [ 2 ] = { 0 } ;
float co [ 2 ] ;
unsigned short mask_short ;
2013-04-29 13:26:43 +00:00
const float brush_alpha = BKE_brush_alpha_get ( ps - > scene , brush ) ;
2014-07-21 12:02:05 +02:00
const float brush_radius = ps - > brush_size ;
2013-04-29 13:26:43 +00:00
const float brush_radius_sq = brush_radius * brush_radius ; /* avoid a square root with every dist comparison */
2013-03-06 19:54:43 +00:00
short lock_alpha = ELEM ( brush - > blend , IMB_BLEND_ERASE_ALPHA , IMB_BLEND_ADD_ALPHA ) ? 0 : brush - > flag & BRUSH_LOCK_ALPHA ;
LinkNode * smearPixels = NULL ;
LinkNode * smearPixels_f = NULL ;
MemArena * smearArena = NULL ; /* mem arena for this brush projection only */
LinkNode * softenPixels = NULL ;
LinkNode * softenPixels_f = NULL ;
MemArena * softenArena = NULL ; /* mem arena for this brush projection only */
if ( tool = = PAINT_TOOL_SMEAR ) {
pos_ofs [ 0 ] = pos [ 0 ] - lastpos [ 0 ] ;
pos_ofs [ 1 ] = pos [ 1 ] - lastpos [ 1 ] ;
2013-10-10 18:18:13 +00:00
smearArena = BLI_memarena_new ( MEM_SIZE_OPTIMAL ( 1 < < 16 ) , " paint smear arena " ) ;
2013-03-06 19:54:43 +00:00
}
else if ( tool = = PAINT_TOOL_SOFTEN ) {
2013-10-10 18:18:13 +00:00
softenArena = BLI_memarena_new ( MEM_SIZE_OPTIMAL ( 1 < < 16 ) , " paint soften arena " ) ;
2013-03-06 19:54:43 +00:00
}
/* 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 ) {
/* No pixels initialized */
project_bucket_init ( ps , thread_index , bucket_index , & 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 ;
2013-10-15 07:18:23 +00:00
is_floatbuf = ( last_projIma - > ibuf - > rect_float ! = NULL ) ;
2013-03-06 19:54:43 +00:00
}
/* end copy */
2014-07-21 12:02:05 +02:00
/* 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 ;
2014-09-17 14:11:37 +10:00
line_len = sqrtf ( line_len ) ;
2014-07-21 12:02:05 +02:00
switch ( brush - > gradient_fill_mode ) {
case BRUSH_GRADIENT_LINEAR :
{
f = dot_v2v2 ( p , tangent ) * line_len_sq_inv ;
break ;
}
case BRUSH_GRADIENT_RADIAL :
{
f = len_v2 ( p ) / line_len ;
break ;
}
}
do_colorband ( 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 {
2014-10-17 14:25:50 +02:00
linearrgb_to_srgb_v3_v3 ( color_f , color_f ) ;
2014-07-21 12:02:05 +02:00
rgba_float_to_uchar ( projPixel - > newColor . ch , color_f ) ;
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 ) ;
2013-04-28 11:55:41 +00:00
2014-07-21 12:02:05 +02:00
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 ;
2013-04-28 11:55:41 +00:00
2014-07-21 12:02:05 +02:00
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 ) ;
}
2013-03-06 19:54:43 +00:00
}
2014-09-04 20:04:03 +02:00
if ( lock_alpha ) {
if ( is_floatbuf ) projPixel - > pixel . f_pt [ 3 ] = projPixel - > origColor . f_pt [ 3 ] ;
else projPixel - > pixel . ch_pt [ 3 ] = projPixel - > origColor . ch_pt [ 3 ] ;
}
2014-07-21 12:02:05 +02:00
last_partial_redraw_cell = last_projIma - > partRedrawRect + projPixel - > bb_cell_index ;
last_partial_redraw_cell - > x1 = min_ii ( last_partial_redraw_cell - > x1 , ( int ) projPixel - > x_px ) ;
last_partial_redraw_cell - > y1 = min_ii ( last_partial_redraw_cell - > y1 , ( int ) projPixel - > y_px ) ;
last_partial_redraw_cell - > x2 = max_ii ( last_partial_redraw_cell - > x2 , ( int ) projPixel - > x_px + 1 ) ;
last_partial_redraw_cell - > y2 = max_ii ( last_partial_redraw_cell - > y2 , ( int ) projPixel - > y_px + 1 ) ;
2013-03-06 19:54:43 +00:00
}
else {
2014-07-21 12:02:05 +02:00
if ( is_floatbuf ) {
/* re-project buffer is assumed byte - TODO, allow float */
bicubic_interpolation_color ( ps - > reproject_ibuf , projPixel - > newColor . ch , NULL ,
projPixel - > projCoSS [ 0 ] , projPixel - > projCoSS [ 1 ] ) ;
if ( projPixel - > newColor . ch [ 3 ] ) {
float newColor_f [ 4 ] ;
float mask = ( ( float ) projPixel - > mask ) * ( 1.0f / 65535.0f ) ;
straight_uchar_to_premul_float ( newColor_f , projPixel - > newColor . ch ) ;
IMB_colormanagement_colorspace_to_scene_linear_v4 ( newColor_f , true , ps - > reproject_ibuf - > rect_colorspace ) ;
mul_v4_v4fl ( newColor_f , newColor_f , mask ) ;
blend_color_mix_float ( projPixel - > pixel . f_pt , projPixel - > origColor . f_pt ,
newColor_f ) ;
}
}
else {
/* re-project buffer is assumed byte - TODO, allow float */
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 ) ;
}
2013-03-06 19:54:43 +00:00
}
}
}
}
else {
/* Normal brush painting */
for ( node = ps - > bucketRect [ bucket_index ] ; node ; node = node - > next ) {
projPixel = ( ProjPixel * ) node - > link ;
2014-02-03 02:46:45 +11:00
dist_sq = len_squared_v2v2 ( projPixel - > projCoSS , pos ) ;
2013-03-06 19:54:43 +00:00
/*if (dist < radius) {*/ /* correct but uses a sqrtf */
2014-02-03 02:46:45 +11:00
if ( dist_sq < = brush_radius_sq ) {
dist = sqrtf ( dist_sq ) ;
2013-03-06 19:54:43 +00:00
2013-04-29 13:26:43 +00:00
falloff = BKE_brush_curve_strength_clamp ( ps - > brush , dist , brush_radius ) ;
2013-03-06 19:54:43 +00:00
if ( falloff > 0.0f ) {
2013-04-28 11:55:41 +00:00
float texrgb [ 3 ] ;
2014-07-21 12:02:05 +02:00
float mask ;
2013-04-30 06:07:42 +00:00
2013-04-29 17:35:50 +00:00
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 . */
2014-07-21 12:02:05 +02:00
float mask_accum = * projPixel - > mask_accum ;
float max_mask = brush_alpha * falloff * 65535.0f ;
2013-04-29 17:35:50 +00:00
2013-05-23 21:02:51 +00:00
if ( ps - > is_maskbrush ) {
float texmask = BKE_brush_sample_masktex ( ps - > scene , ps - > brush , projPixel - > projCoSS , thread_index , pool ) ;
2014-07-21 12:02:05 +02:00
max_mask * = texmask ;
2013-05-23 21:02:51 +00:00
}
2014-07-21 12:02:05 +02:00
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 ) ;
2013-04-29 17:35:50 +00:00
mask_short = ( unsigned short ) mask ;
2014-07-21 12:02:05 +02:00
if ( mask_short > * projPixel - > mask_accum ) {
* projPixel - > mask_accum = mask_short ;
2013-04-29 17:35:50 +00:00
mask = mask_short * ( 1.0f / 65535.0f ) ;
2013-03-06 19:54:43 +00:00
}
else {
/* Go onto the next pixel */
continue ;
}
}
2013-05-23 21:02:51 +00:00
else {
2014-07-21 12:02:05 +02:00
mask = brush_alpha * falloff ;
2013-05-23 21:37:18 +00:00
if ( ps - > is_maskbrush ) {
float texmask = BKE_brush_sample_masktex ( ps - > scene , ps - > brush , projPixel - > projCoSS , thread_index , pool ) ;
2013-05-24 06:50:15 +00:00
CLAMP ( texmask , 0.0f , 1.0f ) ;
2013-05-23 21:37:18 +00:00
mask * = texmask ;
}
2013-05-23 21:02:51 +00:00
}
2013-04-29 17:35:50 +00:00
2013-05-01 03:28:14 +00:00
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 ) ;
mask * = texrgba [ 3 ] ;
}
2013-04-29 17:35:50 +00:00
/* extra mask for normal, layer stencil, .. */
mask * = ( ( float ) projPixel - > mask ) * ( 1.0f / 65535.0f ) ;
2013-03-06 19:54:43 +00:00
2013-04-28 11:55:41 +00:00
if ( mask > 0.0f ) {
2013-03-06 19:54:43 +00:00
/* copy of code above */
if ( last_index ! = projPixel - > image_index ) {
last_index = projPixel - > image_index ;
last_projIma = projImages + last_index ;
last_projIma - > touch = 1 ;
2013-10-15 07:18:23 +00:00
is_floatbuf = ( last_projIma - > ibuf - > rect_float ! = NULL ) ;
2013-03-06 19:54:43 +00:00
}
/* end copy */
2014-07-21 12:02:05 +02:00
/* validate undo tile, since we will modify t*/
* projPixel - > valid = true ;
2013-03-06 19:54:43 +00:00
last_partial_redraw_cell = last_projIma - > partRedrawRect + projPixel - > bb_cell_index ;
last_partial_redraw_cell - > x1 = min_ii ( last_partial_redraw_cell - > x1 , ( int ) projPixel - > x_px ) ;
last_partial_redraw_cell - > y1 = min_ii ( last_partial_redraw_cell - > y1 , ( int ) projPixel - > y_px ) ;
last_partial_redraw_cell - > x2 = max_ii ( last_partial_redraw_cell - > x2 , ( int ) projPixel - > x_px + 1 ) ;
last_partial_redraw_cell - > y2 = max_ii ( last_partial_redraw_cell - > y2 , ( int ) projPixel - > y_px + 1 ) ;
2013-04-28 11:55:41 +00:00
/* texrgb is not used for clone, smear or soften */
2013-03-06 19:54:43 +00:00
switch ( tool ) {
case PAINT_TOOL_CLONE :
2013-04-28 11:55:41 +00:00
if ( is_floatbuf ) do_projectpaint_clone_f ( ps , projPixel , mask ) ;
2013-05-01 02:53:45 +00:00
else do_projectpaint_clone ( ps , projPixel , mask ) ;
2013-03-06 19:54:43 +00:00
break ;
case PAINT_TOOL_SMEAR :
sub_v2_v2v2 ( co , projPixel - > projCoSS , pos_ofs ) ;
2013-04-28 11:55:41 +00:00
if ( is_floatbuf ) do_projectpaint_smear_f ( ps , projPixel , mask , smearArena , & smearPixels_f , co ) ;
2013-05-01 02:53:45 +00:00
else do_projectpaint_smear ( ps , projPixel , mask , smearArena , & smearPixels , co ) ;
2013-03-06 19:54:43 +00:00
break ;
case PAINT_TOOL_SOFTEN :
2013-04-28 11:55:41 +00:00
if ( is_floatbuf ) do_projectpaint_soften_f ( ps , projPixel , mask , softenArena , & softenPixels_f ) ;
2013-05-01 02:53:45 +00:00
else do_projectpaint_soften ( ps , projPixel , mask , softenArena , & softenPixels ) ;
2013-03-06 19:54:43 +00:00
break ;
2014-07-21 12:02:05 +02:00
case PAINT_TOOL_MASK :
if ( is_floatbuf ) do_projectpaint_mask_f ( ps , projPixel , mask ) ;
else do_projectpaint_mask ( ps , projPixel , mask ) ;
break ;
2013-03-06 19:54:43 +00:00
default :
2013-05-01 03:28:14 +00:00
if ( is_floatbuf ) do_projectpaint_draw_f ( ps , projPixel , texrgb , mask ) ;
else do_projectpaint_draw ( ps , projPixel , texrgb , mask ) ;
2013-03-06 19:54:43 +00:00
break ;
}
}
if ( lock_alpha ) {
2014-07-21 12:02:05 +02:00
if ( is_floatbuf ) projPixel - > pixel . f_pt [ 3 ] = projPixel - > origColor . f_pt [ 3 ] ;
else projPixel - > pixel . ch_pt [ 3 ] = projPixel - > origColor . ch_pt [ 3 ] ;
2013-03-06 19:54:43 +00:00
}
/* 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 ;
}
2013-10-15 07:35:12 +00:00
static bool project_paint_op ( void * state , const float lastpos [ 2 ] , const float pos [ 2 ] )
2013-03-06 19:54:43 +00:00
{
/* First unpack args from the struct */
ProjPaintState * ps = ( ProjPaintState * ) state ;
2013-10-15 07:35:12 +00:00
bool touch_any = false ;
2013-03-06 19:54:43 +00:00
ProjectHandle handles [ BLENDER_MAX_THREADS ] ;
ListBase threads ;
int a , i ;
struct ImagePool * pool ;
if ( ! project_bucket_iter_init ( ps , pos ) ) {
return 0 ;
}
if ( ps - > thread_tot > 1 )
BLI_init_threads ( & 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 = ( ProjPaintImage * ) 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 = ( ImagePaintPartialRedraw * ) 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_insert_thread ( & threads , & handles [ a ] ) ;
}
if ( ps - > thread_tot > 1 ) /* wait for everything to be done */
BLI_end_threads ( & 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 ;
}
}
2014-12-29 12:34:46 +01:00
/* calculate pivot for rotation around seletion if needed */
if ( U . uiflag & USER_ORBIT_SELECTION ) {
float w [ 3 ] ;
int side , index ;
index = project_paint_PickFace ( ps , pos , w , & side ) ;
if ( index ! = - 1 ) {
MFace * mf ;
float world [ 3 ] ;
UnifiedPaintSettings * ups = & ps - > scene - > toolsettings - > unified_paint_settings ;
mf = ps - > dm_mface + index ;
2013-03-06 19:54:43 +00:00
2014-12-29 12:34:46 +01:00
if ( side = = 0 ) {
interp_v3_v3v3v3 ( world , ps - > dm_mvert [ ( * ( & mf - > v1 ) ) ] . co , ps - > dm_mvert [ ( * ( & mf - > v2 ) ) ] . co , ps - > dm_mvert [ ( * ( & mf - > v3 ) ) ] . co , w ) ;
}
else {
interp_v3_v3v3v3 ( world , ps - > dm_mvert [ ( * ( & mf - > v1 ) ) ] . co , ps - > dm_mvert [ ( * ( & mf - > v3 ) ) ] . co , ps - > dm_mvert [ ( * ( & mf - > v4 ) ) ] . co , w ) ;
}
ups - > average_stroke_counter + + ;
add_v3_v3 ( ups - > average_stroke_accum , world ) ;
ups - > last_stroke_valid = true ;
}
}
2013-03-06 19:54:43 +00:00
return touch_any ;
}
2014-08-12 14:20:06 +02:00
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 )
2013-03-06 19:54:43 +00:00
{
2013-03-10 17:40:55 +00:00
ProjPaintState * ps = pps ;
2014-07-21 12:02:05 +02:00
Brush * brush = ps - > brush ;
Scene * scene = ps - > scene ;
2013-05-15 14:37:05 +00:00
int a ;
2013-03-06 19:54:43 +00:00
2014-07-21 12:02:05 +02:00
ps - > brush_size = size ;
2014-08-12 14:20:06 +02:00
ps - > blend = brush - > blend ;
if ( eraser )
ps - > blend = IMB_BLEND_ERASE_ALPHA ;
2013-03-17 03:08:46 +00:00
/* 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 ;
2013-10-26 04:07:18 +00:00
float * cursor = ED_view3d_cursor3d_get ( scene , v3d ) ;
2013-05-15 11:16:01 +00:00
int mval_i [ 2 ] = { ( int ) pos [ 0 ] , ( int ) pos [ 1 ] } ;
2013-03-17 03:08:46 +00:00
view3d_operator_needs_opengl ( C ) ;
2013-09-16 08:59:54 +00:00
if ( ! ED_view3d_autodist ( scene , ps - > ar , v3d , mval_i , cursor , false , NULL ) )
2013-05-15 14:37:05 +00:00
return ;
2013-03-17 03:08:46 +00:00
ED_region_tag_redraw ( ps - > ar ) ;
2013-05-15 14:37:05 +00:00
return ;
2013-03-17 03:08:46 +00:00
}
2014-07-21 12:02:05 +02:00
/* handle gradient and inverted stroke color here */
if ( ps - > tool = = PAINT_TOOL_DRAW ) {
paint_brush_color_get ( scene , brush , false , ps - > mode = = BRUSH_STROKE_INVERT , distance , pressure , ps - > paint_color , NULL ) ;
srgb_to_linearrgb_v3_v3 ( ps - > paint_color_linear , ps - > paint_color ) ;
}
else if ( ps - > tool = = PAINT_TOOL_FILL ) {
copy_v3_v3 ( ps - > paint_color , BKE_brush_color_get ( scene , brush ) ) ;
srgb_to_linearrgb_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 ;
}
}
2013-05-15 14:37:05 +00:00
/* continue adding to existing partial redraw rects until redraw */
if ( ! ps - > need_redraw ) {
for ( a = 0 ; a < ps - > image_tot ; a + + )
partial_redraw_array_init ( ps - > projImages [ a ] . partRedrawRect ) ;
}
2013-03-06 19:54:43 +00:00
2013-05-15 14:37:05 +00:00
if ( project_paint_op ( ps , prev_pos , pos ) )
ps - > need_redraw = true ;
2013-03-06 19:54:43 +00:00
}
/* initialize project paint settings from context */
2013-03-17 03:08:46 +00:00
static void project_state_init ( bContext * C , Object * ob , ProjPaintState * ps , int mode )
2013-03-06 19:54:43 +00:00
{
Scene * scene = CTX_data_scene ( C ) ;
ToolSettings * settings = scene - > toolsettings ;
/* brush */
2013-03-17 03:08:46 +00:00
ps - > mode = mode ;
Paint refactoring commit, non-disruptive (in theory :p)
* Fix precision overflow issue with overlay previews,
* Expose alpha mask mapping to UI (still not functional but coming soon).
* More overlay refactoring:
Overlay now does minimal checking for texture refresh.
Instead, we now have invalidation flags to set an aspect of the brush
overlay as invalid. This is necessary because this way we will be able to
separate and preview different brush attributes on the overlays, using
different textures:
These attributes/aspects are:
Primary texture (main texture for sculpt, vertex, imapaint)
Secondary texture (mask/alpha texture for imapaint)
Cursor texture (cursor texture. It involves brush strength and curves)
Modified the relevant RNA property update functions and C update callback
functions to call the relevant cursor invalidation functions instead
of checking every frame for multiple properties.
Properties that affect this are:
Image changes, if image is used by current brush,
Texture slot changes, similarly
Curve changes,
Object mode change invalidates the cursor
Paint tool change invalidates the cursor.
These changes give slightly more invalidation cases than simply
comparing the relevant properties each frame, but these do not occur in
performance critical moments and it's a much more elegant system than
adding more variables to check per frame each time we add something on
the system.
2013-04-12 17:21:31 +00:00
ps - > brush = BKE_paint_brush ( & settings - > imapaint . paint ) ;
2013-03-06 19:54:43 +00:00
if ( ps - > brush ) {
Brush * brush = ps - > brush ;
ps - > tool = brush - > imagepaint_tool ;
ps - > blend = brush - > blend ;
2014-07-21 12:02:05 +02:00
/* 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 ) ;
2014-08-27 15:07:57 +02:00
ps - > blurkernel = paint_new_blur_kernel ( brush , true ) ;
2014-07-21 12:02:05 +02:00
}
2013-03-06 19:54:43 +00:00
/* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */
2014-07-21 12:02:05 +02:00
ps - > do_masking = paint_use_opacity_masking ( brush ) ;
2013-03-25 01:00:16 +00:00
ps - > is_texbrush = ( brush - > mtex . tex & & brush - > imagepaint_tool = = PAINT_TOOL_DRAW ) ? true : false ;
2014-07-21 12:02:05 +02:00
ps - > is_maskbrush = ( brush - > mask_mtex . tex ) ? true : false ;
2013-03-06 19:54:43 +00:00
}
else {
/* brush may be NULL*/
ps - > do_masking = false ;
ps - > is_texbrush = false ;
2013-03-25 01:00:16 +00:00
ps - > is_maskbrush = false ;
2013-03-06 19:54:43 +00:00
}
/* 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 - > scene = scene ;
ps - > ob = ob ; /* allow override of active object */
2014-08-28 16:40:38 +02:00
ps - > do_material_slots = ( settings - > imapaint . mode = = IMAGEPAINT_MODE_MATERIAL ) ;
2014-07-21 12:02:05 +02:00
ps - > stencil_ima = settings - > imapaint . stencil ;
2014-08-28 16:40:38 +02:00
ps - > canvas_ima = ( ! ps - > do_material_slots ) ?
2014-08-29 15:35:19 +10:00
settings - > imapaint . canvas : NULL ;
2014-08-28 16:40:38 +02:00
ps - > clone_ima = ( ! ps - > do_material_slots ) ?
2014-08-29 15:35:19 +10:00
settings - > imapaint . clone : NULL ;
2014-08-28 16:40:38 +02:00
2013-03-06 19:54:43 +00:00
/* setup projection painting data */
2014-09-21 01:37:40 +02:00
if ( ps - > tool ! = PAINT_TOOL_FILL ) {
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 ;
}
else {
ps - > do_backfacecull = ps - > do_occlude = ps - > do_mask_normal = 0 ;
}
2013-03-06 19:54:43 +00:00
ps - > do_new_shading_nodes = BKE_scene_use_new_shading_nodes ( scene ) ; /* only cache the value */
if ( ps - > tool = = PAINT_TOOL_CLONE )
2014-02-03 18:55:59 +11:00
ps - > do_layer_clone = ( settings - > imapaint . flag & IMAGEPAINT_PROJECT_LAYER_CLONE ) ? 1 : 0 ;
2013-03-06 19:54:43 +00:00
2014-09-10 23:02:31 +10:00
ps - > do_stencil_brush = ( ps - > brush & & ps - > brush - > imagepaint_tool = = PAINT_TOOL_MASK ) ;
2014-07-21 12:02:05 +02:00
/* 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 ) ;
2013-03-06 19:54:43 +00:00
# ifndef PROJ_DEBUG_NOSEAMBLEED
ps - > seam_bleed_px = settings - > imapaint . seam_bleed ; /* pixel num to 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 ;
}
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 )
2014-04-01 11:34:00 +11:00
ps - > do_mask_normal = false ; /* no need to do blending */
2013-03-10 17:40:55 +00:00
return ;
2013-03-06 19:54:43 +00:00
}
2013-05-15 11:16:01 +00:00
void * paint_proj_new_stroke ( bContext * C , Object * ob , const float mouse [ 2 ] , int mode )
2013-03-11 16:23:33 +00:00
{
2013-03-10 17:40:55 +00:00
ProjPaintState * ps = MEM_callocN ( sizeof ( ProjPaintState ) , " ProjectionPaintState " ) ;
2014-07-21 12:02:05 +02:00
2013-03-17 03:08:46 +00:00
project_state_init ( C , ob , ps , mode ) ;
if ( ps - > tool = = PAINT_TOOL_CLONE & & mode = = BRUSH_STROKE_INVERT ) {
view3d_operator_needs_opengl ( C ) ;
return ps ;
}
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
paint_brush_init_tex ( ps - > brush ) ;
2013-03-06 19:54:43 +00:00
2014-12-18 16:28:29 +01:00
ps - > source = ( ps - > tool = = PAINT_TOOL_FILL ) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW ;
2013-03-10 17:40:55 +00:00
if ( ps - > ob = = NULL | | ! ( ps - > ob - > lay & ps - > v3d - > lay ) ) {
MEM_freeN ( ps ) ;
return NULL ;
2013-03-06 19:54:43 +00:00
}
2013-03-10 17:40:55 +00:00
ps - > orig_brush_size = BKE_brush_size_get ( ps - > scene , ps - > brush ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
/* Don't allow brush size below 2 */
if ( BKE_brush_size_get ( ps - > scene , ps - > brush ) < 2 )
BKE_brush_size_set ( ps - > scene , ps - > brush , 2 ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
/* allocate and initialize spatial data structures */
project_paint_begin ( ps ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
if ( ps - > dm = = NULL ) {
MEM_freeN ( ps ) ;
return NULL ;
}
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
paint_proj_begin_clone ( ps , mouse ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
return ps ;
}
2013-03-06 19:54:43 +00:00
2013-05-15 14:37:05 +00:00
void paint_proj_redraw ( const bContext * C , void * pps , bool final )
{
ProjPaintState * ps = pps ;
if ( ps - > need_redraw ) {
project_image_refresh_tagged ( ps ) ;
ps - > 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 ) ) ;
}
}
2013-03-10 17:40:55 +00:00
void paint_proj_stroke_done ( void * pps )
{
ProjPaintState * ps = pps ;
2013-03-17 03:08:46 +00:00
if ( ps - > tool = = PAINT_TOOL_CLONE & & ps - > mode = = BRUSH_STROKE_INVERT ) {
MEM_freeN ( ps ) ;
return ;
}
2013-03-10 17:40:55 +00:00
BKE_brush_size_set ( ps - > scene , ps - > brush , ps - > orig_brush_size ) ;
2013-03-17 03:08:46 +00:00
2013-03-10 17:40:55 +00:00
paint_brush_exit_tex ( ps - > brush ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
project_paint_end ( ps ) ;
MEM_freeN ( ps ) ;
}
/* 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 ) - > image , RNA_enum_get ( op - > ptr , " image " ) ) ;
Scene * scene = CTX_data_scene ( C ) ;
ProjPaintState ps = { NULL } ;
int orig_brush_size ;
IDProperty * idgroup ;
IDProperty * view_data = NULL ;
2014-07-21 12:02:05 +02:00
Object * ob = OBACT ;
2014-10-06 15:12:06 +02:00
bool uvs , mat , tex ;
2013-03-06 19:54:43 +00:00
2014-09-10 23:02:31 +10:00
if ( ob = = NULL | | ob - > type ! = OB_MESH ) {
2013-03-10 17:40:55 +00:00
BKE_report ( op - > reports , RPT_ERROR , " No active mesh object " ) ;
return OPERATOR_CANCELLED ;
2013-03-06 19:54:43 +00:00
}
2014-10-06 15:12:06 +02:00
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 ;
}
2014-09-10 23:02:31 +10:00
project_state_init ( C , ob , & ps , BRUSH_STROKE_NORMAL ) ;
2013-03-10 17:40:55 +00:00
if ( image = = NULL ) {
BKE_report ( op - > reports , RPT_ERROR , " Image could not be found " ) ;
return OPERATOR_CANCELLED ;
}
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
ps . reproject_image = image ;
ps . reproject_ibuf = BKE_image_acquire_ibuf ( image , NULL , NULL ) ;
if ( ps . reproject_ibuf = = NULL | | ps . reproject_ibuf - > rect = = NULL ) {
BKE_report ( op - > reports , RPT_ERROR , " Image data could not be found " ) ;
return OPERATOR_CANCELLED ;
2013-03-06 19:54:43 +00:00
}
2013-03-10 17:40:55 +00:00
idgroup = IDP_GetProperties ( & image - > id , 0 ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
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 ;
}
}
2013-03-10 02:30:53 +00:00
2013-03-10 17:40:55 +00:00
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 ;
}
}
2013-03-10 02:30:53 +00:00
2013-03-10 17:40:55 +00:00
/* override */
2013-03-25 01:00:16 +00:00
ps . is_texbrush = false ;
ps . is_maskbrush = false ;
2013-03-10 17:40:55 +00:00
ps . do_masking = false ;
orig_brush_size = BKE_brush_size_get ( scene , ps . brush ) ;
BKE_brush_size_set ( scene , ps . brush , 32 ) ; /* cover the whole image */
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
ps . tool = PAINT_TOOL_DRAW ; /* so pixels are initialized with minimal info */
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
scene - > toolsettings - > imapaint . flag | = IMAGEPAINT_DRAWING ;
2013-03-10 02:30:53 +00:00
2013-12-05 22:28:14 +02:00
ED_undo_paint_push_begin ( UNDO_PAINT_IMAGE , op - > type - > name ,
2014-07-21 12:02:05 +02:00
ED_image_undo_restore , ED_image_undo_free , NULL ) ;
2013-03-06 22:54:44 +00:00
2013-03-10 17:40:55 +00:00
/* allocate and initialize spatial data structures */
project_paint_begin ( & ps ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
if ( ps . dm = = NULL ) {
BKE_brush_size_set ( scene , ps . brush , orig_brush_size ) ;
return OPERATOR_CANCELLED ;
2013-03-06 19:54:43 +00:00
}
else {
2013-03-10 17:40:55 +00:00
float pos [ 2 ] = { 0.0 , 0.0 } ;
float lastpos [ 2 ] = { 0.0 , 0.0 } ;
int a ;
for ( a = 0 ; a < ps . image_tot ; a + + )
partial_redraw_array_init ( ps . projImages [ a ] . partRedrawRect ) ;
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 ) ;
}
2013-03-06 19:54:43 +00:00
}
2013-03-10 17:40:55 +00:00
project_paint_end ( & ps ) ;
scene - > toolsettings - > imapaint . flag & = ~ IMAGEPAINT_DRAWING ;
BKE_brush_size_set ( scene , ps . brush , orig_brush_size ) ;
return OPERATOR_FINISHED ;
}
2013-03-10 02:30:53 +00:00
2013-03-10 17:40:55 +00:00
void PAINT_OT_project_image ( wmOperatorType * ot )
{
PropertyRNA * prop ;
2013-03-10 02:30:53 +00:00
2013-03-10 17:40:55 +00:00
/* 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 ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
/* 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 ) ;
2014-05-05 15:09:29 +10:00
RNA_def_property_flag ( prop , PROP_ENUM_NO_TRANSLATE ) ;
2013-03-10 17:40:55 +00:00
ot - > prop = prop ;
2013-03-06 19:54:43 +00:00
}
2013-03-10 17:40:55 +00:00
static int texture_paint_image_from_view_exec ( bContext * C , wmOperator * op )
2013-03-06 19:54:43 +00:00
{
2013-03-10 17:40:55 +00:00
Image * image ;
ImBuf * ibuf ;
char filename [ FILE_MAX ] ;
2013-03-06 19:54:43 +00:00
Scene * scene = CTX_data_scene ( C ) ;
ToolSettings * settings = scene - > toolsettings ;
2013-03-10 17:40:55 +00:00
int w = settings - > imapaint . screen_grab_size [ 0 ] ;
int h = settings - > imapaint . screen_grab_size [ 1 ] ;
int maxsize ;
char err_out [ 256 ] = " unknown " ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
RNA_string_get ( op - > ptr , " filepath " , filename ) ;
2013-03-06 19:54:43 +00:00
2013-04-12 17:56:07 +00:00
maxsize = GPU_max_texture_size ( ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
if ( w > maxsize ) w = maxsize ;
if ( h > maxsize ) h = maxsize ;
2013-03-06 19:54:43 +00:00
2014-04-30 07:48:54 +10:00
ibuf = ED_view3d_draw_offscreen_imbuf ( scene , CTX_wm_view3d ( C ) , CTX_wm_region ( C ) , w , h , IB_rect , false , R_ALPHAPREMUL , err_out ) ;
2013-03-10 17:40:55 +00:00
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 ;
2013-03-08 04:00:06 +00:00
}
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
image = BKE_image_add_from_imbuf ( ibuf ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
if ( image ) {
/* now for the trickyness. store the view projection here!
* re - projection will reuse this */
View3D * v3d = CTX_wm_view3d ( C ) ;
RegionView3D * rv3d = CTX_wm_region_view3d ( C ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
IDPropertyTemplate val ;
IDProperty * idgroup = IDP_GetProperties ( & image - > id , 1 ) ;
IDProperty * view_data ;
2013-03-14 07:56:40 +00:00
bool is_ortho ;
2013-03-10 17:40:55 +00:00
float * array ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
val . array . len = PROJ_VIEW_DATA_SIZE ;
val . array . type = IDP_FLOAT ;
view_data = IDP_New ( IDP_ARRAY , & val , PROJ_VIEW_DATA_ID ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
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 ) ;
2013-03-14 07:56:40 +00:00
is_ortho = ED_view3d_clip_range_get ( v3d , rv3d , & array [ 0 ] , & array [ 1 ] , true ) ;
array [ 2 ] = is_ortho ? 1.0f : 0.0f ; /* using float for a bool is dodgy but since its an extra member in the array... easier then adding a single bool prop */
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
IDP_AddToGroup ( idgroup , view_data ) ;
2013-03-06 19:54:43 +00:00
2013-03-10 17:40:55 +00:00
rename_id ( & image - > id , " image_view " ) ;
2013-03-06 19:54:43 +00:00
}
2013-03-10 17:40:55 +00:00
return OPERATOR_FINISHED ;
2013-03-06 19:54:43 +00:00
}
2013-03-10 17:40:55 +00:00
void PAINT_OT_image_from_view ( wmOperatorType * ot )
2013-03-06 19:54:43 +00:00
{
/* identifiers */
2013-03-10 17:40:55 +00:00
ot - > name = " Image from View " ;
ot - > idname = " PAINT_OT_image_from_view " ;
ot - > description = " Make an image from the current 3D view for re-projection " ;
2013-03-06 19:54:43 +00:00
/* api callbacks */
2013-03-10 17:40:55 +00:00
ot - > exec = texture_paint_image_from_view_exec ;
ot - > poll = ED_operator_region_view3d_active ;
2013-03-06 19:54:43 +00:00
/* flags */
2013-03-10 17:40:55 +00:00
ot - > flag = OPTYPE_REGISTER ;
2013-03-06 19:54:43 +00:00
2014-01-16 21:43:22 +11:00
RNA_def_string_file_name ( ot - > srna , " filepath " , NULL , FILE_MAX , " File Path " , " Name of the file " ) ;
2013-03-06 19:54:43 +00:00
}
2014-07-21 12:02:05 +02:00
2014-09-05 15:48:03 +02:00
/*********************************************
* Data generation for projective texturing *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-10-06 15:12:06 +02:00
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, " : " "
) ;
}
2014-09-05 15:48:03 +02:00
/* Make sure that active object has a material, and assign UVs and image layers if they do not exist */
2014-10-06 15:12:06 +02:00
bool BKE_paint_proj_mesh_data_check ( Scene * scene , Object * ob , bool * uvs , bool * mat , bool * tex , bool * stencil )
2014-09-05 15:48:03 +02:00
{
Mesh * me ;
int layernum ;
2014-10-06 15:12:06 +02:00
ImagePaintSettings * imapaint = & scene - > toolsettings - > imapaint ;
2014-09-05 15:48:03 +02:00
Brush * br = BKE_paint_brush ( & imapaint - > paint ) ;
2014-10-06 15:12:06 +02:00
bool hasmat = true ;
bool hastex = true ;
bool hasstencil = true ;
bool hasuvs = true ;
2014-09-05 15:48:03 +02:00
2014-10-06 15:12:06 +02:00
imapaint - > missing_data = 0 ;
2014-09-10 23:02:31 +10:00
BLI_assert ( ob - > type = = OB_MESH ) ;
2014-10-06 15:12:06 +02:00
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 ;
2014-09-05 15:48:03 +02:00
if ( ! ma - > texpaintslot ) {
/* refresh here just in case */
BKE_texpaint_slot_refresh_cache ( scene , ma ) ;
/* if still no slots, we have to add */
2014-10-06 15:12:06 +02:00
if ( ma - > texpaintslot ) {
hastex = true ;
break ;
2014-09-05 15:48:03 +02:00
}
}
2014-10-06 15:12:06 +02:00
else {
hastex = true ;
break ;
}
2014-09-05 15:48:03 +02:00
}
}
}
}
2014-10-06 15:12:06 +02:00
else if ( imapaint - > mode = = IMAGEPAINT_MODE_IMAGE ) {
2014-09-05 15:48:03 +02:00
if ( imapaint - > canvas = = NULL ) {
2014-10-06 15:12:06 +02:00
hastex = false ;
2014-09-05 15:48:03 +02:00
}
}
2014-10-06 15:12:06 +02:00
2014-09-05 15:48:03 +02:00
me = BKE_mesh_from_object ( ob ) ;
layernum = CustomData_number_of_layers ( & me - > pdata , CD_MTEXPOLY ) ;
if ( layernum = = 0 ) {
2014-10-06 15:12:06 +02:00
hasuvs = false ;
2014-09-05 15:48:03 +02:00
}
/* 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 ) {
2014-10-06 15:12:06 +02:00
hasstencil = false ;
2014-09-05 15:48:03 +02:00
}
}
2014-10-06 15:12:06 +02:00
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 ;
2014-09-05 15:48:03 +02:00
}
2014-07-21 12:02:05 +02:00
/* Add layer operator */
static EnumPropertyItem layer_type_items [ ] = {
{ MAP_COL , " DIFFUSE_COLOR " , 0 , " Diffuse Color " , " " } ,
{ MAP_REF , " DIFFUSE_INTENSITY " , 0 , " Diffuse Intensity " , " " } ,
{ MAP_ALPHA , " ALPHA " , 0 , " Alpha " , " " } ,
{ MAP_TRANSLU , " TRANSLUCENCY " , 0 , " Translucency " , " " } ,
{ MAP_COLSPEC , " SPECULAR_COLOR " , 0 , " Specular Color " , " " } ,
{ MAP_SPEC , " SPECULAR_INTENSITY " , 0 , " Specular Intensity " , " " } ,
{ MAP_HAR , " SPECULAR_HARDNESS " , 0 , " Specular Hardness " , " " } ,
{ MAP_AMB , " AMBIENT " , 0 , " Ambient " , " " } ,
2014-10-13 12:27:57 +02:00
{ MAP_EMIT , " EMIT " , 0 , " Emit " , " " } ,
2014-07-21 12:02:05 +02:00
{ MAP_COLMIR , " MIRROR_COLOR " , 0 , " Mirror Color " , " " } ,
{ MAP_RAYMIRR , " RAYMIRROR " , 0 , " Ray Mirror " , " " } ,
{ MAP_NORM , " NORMAL " , 0 , " Normal " , " " } ,
{ MAP_WARP , " WARP " , 0 , " Warp " , " " } ,
{ MAP_DISPLACE , " DISPLACE " , 0 , " Displace " , " " } ,
{ 0 , NULL , 0 , NULL , NULL }
} ;
2014-10-06 15:12:06 +02:00
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 ) ;
return ima ;
}
static bool proj_paint_add_slot ( bContext * C , wmOperator * op )
2014-07-21 12:02:05 +02:00
{
Object * ob = CTX_data_active_object ( C ) ;
Scene * scene = CTX_data_scene ( C ) ;
2014-10-06 15:12:06 +02:00
Material * ma ;
2014-10-23 13:39:45 +02:00
bool is_bi = BKE_scene_uses_blender_internal ( scene ) | | BKE_scene_uses_blender_game ( scene ) ;
2014-10-06 15:12:06 +02:00
Image * ima = NULL ;
2014-07-21 12:02:05 +02:00
if ( ! ob )
return false ;
2014-10-06 15:12:06 +02:00
ma = give_current_material ( ob , ob - > actcol ) ;
2014-07-21 12:02:05 +02:00
if ( ma ) {
2014-10-06 15:12:06 +02:00
Main * bmain = CTX_data_main ( C ) ;
if ( ! is_bi & & BKE_scene_use_new_shading_nodes ( scene ) ) {
bNode * imanode ;
bNodeTree * ntree = ma - > nodetree ;
2014-07-21 12:02:05 +02:00
2014-10-06 15:12:06 +02:00
if ( ! ntree ) {
ED_node_shader_default ( C , & ma - > id ) ;
ntree = ma - > nodetree ;
}
ma - > use_nodes = true ;
/* 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 ;
nodeSetActive ( ntree , imanode ) ;
ntreeUpdateTree ( CTX_data_main ( C ) , ntree ) ;
2014-07-21 12:02:05 +02:00
}
else {
MTex * mtex = add_mtex_id ( & ma - > id , - 1 ) ;
/* successful creation of mtex layer, now create set */
if ( mtex ) {
2014-07-24 12:01:25 +02:00
int type = MAP_COL ;
2014-08-25 00:34:24 +02:00
int type_id = 0 ;
2014-07-21 12:02:05 +02:00
2014-08-25 00:34:24 +02:00
if ( op ) {
int i ;
2014-07-24 12:01:25 +02:00
type = RNA_enum_get ( op - > ptr , " type " ) ;
2014-07-21 12:02:05 +02:00
2014-08-25 00:34:24 +02:00
for ( i = 0 ; i < ARRAY_SIZE ( layer_type_items ) ; i + + ) {
if ( layer_type_items [ i ] . value = = type ) {
type_id = i ;
break ;
}
}
}
mtex - > tex = add_texture ( bmain , DATA_ ( layer_type_items [ type_id ] . name ) ) ;
2014-07-21 12:02:05 +02:00
mtex - > mapto = type ;
if ( mtex - > tex ) {
2014-10-06 15:12:06 +02:00
ima = mtex - > tex - > ima = proj_paint_image_create ( op , bmain ) ;
2014-07-21 12:02:05 +02:00
}
2014-10-20 20:08:22 +02:00
WM_event_add_notifier ( C , NC_TEXTURE | NA_ADDED , mtex - > tex ) ;
2014-07-21 12:02:05 +02:00
}
2014-10-06 15:12:06 +02:00
}
if ( ima ) {
BKE_texpaint_slot_refresh_cache ( scene , ma ) ;
BKE_image_signal ( ima , NULL , IMA_SIGNAL_USER_NEW_IMAGE ) ;
WM_event_add_notifier ( C , NC_IMAGE | NA_ADDED , ima ) ;
DAG_id_tag_update ( & ma - > id , 0 ) ;
ED_area_tag_redraw ( CTX_wm_area ( C ) ) ;
2014-11-24 10:42:14 +01:00
BKE_paint_proj_mesh_data_check ( scene , ob , NULL , NULL , NULL , NULL ) ;
2014-10-06 21:49:38 +02:00
return true ;
2014-07-21 12:02:05 +02:00
}
}
2014-10-06 15:12:06 +02:00
2014-07-21 12:02:05 +02:00
return false ;
}
static int texture_paint_add_texture_paint_slot_exec ( bContext * C , wmOperator * op )
{
2014-10-06 21:49:38 +02:00
if ( proj_paint_add_slot ( C , op ) ) {
2014-10-06 15:12:06 +02:00
return OPERATOR_FINISHED ;
2014-10-06 21:49:38 +02:00
}
else {
return OPERATOR_CANCELLED ;
}
2014-07-24 11:39:04 +02:00
}
static int texture_paint_add_texture_paint_slot_invoke ( bContext * C , wmOperator * op , const wmEvent * UNUSED ( event ) )
{
2014-07-24 12:01:25 +02:00
char imagename [ MAX_ID_NAME - 2 ] ;
Object * ob = CTX_data_active_object ( C ) ;
Material * ma = give_current_material ( ob , ob - > actcol ) ;
int type = RNA_enum_get ( op - > ptr , " type " ) ;
2014-10-06 15:12:06 +02:00
if ( ! ma ) {
ma = BKE_material_add ( CTX_data_main ( C ) , " Material " ) ;
/* no material found, just assign to first slot */
assign_material ( ob , ma , ob - > actcol , BKE_MAT_ASSIGN_USERPREF ) ;
}
2014-07-24 12:01:25 +02:00
type = RNA_enum_from_value ( layer_type_items , type ) ;
/* get the name of the texture layer type */
BLI_assert ( type ! = - 1 ) ;
/* take the second letter to avoid the ID identifier */
BLI_snprintf ( imagename , FILE_MAX , " %s %s " , & ma - > id . name [ 2 ] , layer_type_items [ type ] . name ) ;
RNA_string_set ( op - > ptr , " name " , imagename ) ;
2014-07-24 11:39:04 +02:00
return WM_operator_props_dialog_popup ( C , op , 15 * UI_UNIT_X , 5 * UI_UNIT_Y ) ;
2014-07-21 12:02:05 +02:00
}
2014-07-24 12:01:25 +02:00
# define IMA_DEF_NAME N_("Untitled")
2014-07-24 11:39:04 +02:00
2014-07-21 12:02:05 +02:00
void PAINT_OT_add_texture_paint_slot ( wmOperatorType * ot )
{
2014-07-24 11:39:04 +02:00
PropertyRNA * prop ;
2014-08-12 16:17:07 +02:00
static float default_color [ 4 ] = { 0.0f , 0.0f , 0.0f , 1.0f } ;
2014-07-24 11:39:04 +02:00
2014-07-21 12:02:05 +02:00
/* identifiers */
ot - > name = " Add Texture Paint Slot " ;
ot - > description = " Add a texture paint slot " ;
ot - > idname = " PAINT_OT_add_texture_paint_slot " ;
/* api callbacks */
2014-07-24 11:39:04 +02:00
ot - > invoke = texture_paint_add_texture_paint_slot_invoke ;
2014-07-21 12:02:05 +02:00
ot - > exec = texture_paint_add_texture_paint_slot_exec ;
ot - > poll = ED_operator_region_view3d_active ;
/* flags */
2014-10-20 20:08:22 +02:00
ot - > flag = OPTYPE_UNDO ;
2014-07-21 12:02:05 +02:00
/* properties */
2014-07-24 11:39:04 +02:00
prop = RNA_def_enum ( ot - > srna , " type " , layer_type_items , 0 , " Type " , " Merge method to use " ) ;
RNA_def_property_flag ( prop , PROP_HIDDEN ) ;
2014-07-24 12:01:25 +02:00
RNA_def_string ( ot - > srna , " name " , IMA_DEF_NAME , MAX_ID_NAME - 2 , " Name " , " Image datablock name " ) ;
2014-07-24 11:39:04 +02:00
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 " , 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 " ) ;
2014-07-21 12:02:05 +02:00
}
2014-08-26 19:00:25 +02:00
static int texture_paint_delete_texture_paint_slot_exec ( bContext * C , wmOperator * UNUSED ( op ) )
{
Object * ob = CTX_data_active_object ( C ) ;
Scene * scene = CTX_data_scene ( C ) ;
Material * ma ;
2014-10-23 13:39:45 +02:00
bool is_bi = BKE_scene_uses_blender_internal ( scene ) | | BKE_scene_uses_blender_game ( scene ) ;
2014-08-26 19:00:25 +02:00
TexPaintSlot * slot ;
/* not supported for node-based engines */
2014-08-27 18:52:17 +02:00
if ( ! ob | | ! is_bi )
2014-08-26 19:00:25 +02:00
return OPERATOR_CANCELLED ;
ma = give_current_material ( ob , ob - > actcol ) ;
2014-08-27 16:09:24 +02:00
if ( ! ma - > texpaintslot | | ma - > use_nodes )
2014-08-26 19:00:25 +02:00
return OPERATOR_CANCELLED ;
slot = ma - > texpaintslot + ma - > paint_active_slot ;
2014-08-28 15:48:26 +02:00
if ( ma - > mtex [ slot - > index ] - > tex )
id_us_min ( & ma - > mtex [ slot - > index ] - > tex - > id ) ;
MEM_freeN ( ma - > mtex [ slot - > index ] ) ;
ma - > mtex [ slot - > index ] = NULL ;
2014-08-26 19:00:25 +02:00
2014-08-28 15:48:26 +02:00
BKE_texpaint_slot_refresh_cache ( scene , ma ) ;
DAG_id_tag_update ( & ma - > id , 0 ) ;
2014-10-31 14:37:36 +01:00
WM_event_add_notifier ( C , NC_MATERIAL , ma ) ;
2014-08-28 15:48:26 +02:00
/* we need a notifier for data change since we change the displayed modifier uvs */
2014-10-31 14:37:36 +01:00
WM_event_add_notifier ( C , NC_GEOM | ND_DATA , ob - > data ) ;
2014-08-28 15:48:26 +02:00
return OPERATOR_FINISHED ;
2014-08-26 19:00:25 +02:00
}
void PAINT_OT_delete_texture_paint_slot ( wmOperatorType * ot )
{
/* identifiers */
ot - > name = " Delete Texture Paint Slot " ;
2014-10-04 12:21:11 +02:00
ot - > description = " Delete selected texture paint slot " ;
2014-08-26 19:00:25 +02:00
ot - > idname = " PAINT_OT_delete_texture_paint_slot " ;
/* api callbacks */
ot - > exec = texture_paint_delete_texture_paint_slot_exec ;
ot - > poll = ED_operator_region_view3d_active ;
/* flags */
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO ;
}
2014-10-31 14:37:36 +01:00
static int add_simple_uvs_exec ( bContext * C , wmOperator * UNUSED ( op ) )
{
/* no checks here, poll function does them for us */
Object * ob = CTX_data_active_object ( C ) ;
Scene * scene = CTX_data_scene ( C ) ;
Mesh * me = ob - > data ;
bool synch_selection = ( scene - > toolsettings - > uv_flag & UV_SYNC_SELECTION ) ! = 0 ;
BMesh * bm = BM_mesh_create ( & bm_mesh_allocsize_default ) ;
/* turn synch selection off, since we are not in edit mode we need to ensure only the uv flags are tested */
scene - > toolsettings - > uv_flag & = ~ UV_SYNC_SELECTION ;
ED_mesh_uv_texture_ensure ( me , NULL ) ;
BM_mesh_bm_from_me ( bm , me , true , false , 0 ) ;
/* select all uv loops first - pack parameters needs this to make sure charts are registered */
ED_uvedit_select_all ( bm ) ;
2014-10-31 15:32:59 +01:00
ED_uvedit_unwrap_cube_project ( ob , bm , 1.0 , false ) ;
2014-10-31 14:37:36 +01:00
/* set the margin really quickly before the packing operation*/
scene - > toolsettings - > uvcalc_margin = 0.001f ;
ED_uvedit_pack_islands ( scene , ob , bm , false , false , true ) ;
BM_mesh_bm_to_me ( bm , me , false ) ;
BM_mesh_free ( bm ) ;
if ( synch_selection )
scene - > toolsettings - > uv_flag | = UV_SYNC_SELECTION ;
2014-11-24 10:42:14 +01:00
BKE_paint_proj_mesh_data_check ( scene , ob , NULL , NULL , NULL , NULL ) ;
2014-10-31 14:37:36 +01:00
DAG_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 int add_simple_uvs_poll ( bContext * C )
{
Object * ob = CTX_data_active_object ( C ) ;
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 " ;
/* api callbacks */
ot - > exec = add_simple_uvs_exec ;
ot - > poll = add_simple_uvs_poll ;
/* flags */
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO ;
}