diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-01-18 01:08:25 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-01-18 01:08:25 +0400 |
commit | 0f28c1c27aec41dd22e31aac2c02cdfae785dd1b (patch) | |
tree | 1c41b37d29a56390d810846cd7fe8f1218e40576 /source/blender/editors/sculpt_paint/sculpt_uv.c | |
parent | 9be40c026df54fee796c4073ebd4734c55ed5807 (diff) | |
parent | 408f7963c5cfd65792d826390b01896282de8e97 (diff) |
svn merge ^/trunk/blender -r43461:43472
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_uv.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_uv.c | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c new file mode 100644 index 00000000000..21bbb014eb0 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -0,0 +1,787 @@ +/* + * ***** 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation, 2002-2009 + * All rights reserved. + * + * Contributor(s): Antony Riakiotakis + * + * ***** END GPL LICENSE BLOCK ***** + * + * UV Sculpt tools + * + */ + +/** \file blender/editors/sculpt_paint/sculpt_uv.c + * \ingroup edsculpt + */ + + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_editVert.h" +#include "BLI_math.h" +#include "BLI_ghash.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_brush_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_brush.h" +#include "BKE_paint.h" +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_depsgraph.h" +#include "BKE_mesh.h" +#include "BKE_customdata.h" +#include "BKE_tessmesh.h" + +#include "ED_screen.h" +#include "ED_image.h" +#include "ED_mesh.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "paint_intern.h" +#include "uvedit_intern.h" + +#include "UI_view2d.h" + +#define MARK_BOUNDARY 1 + +typedef struct UvAdjacencyElement { + /* pointer to original uvelement */ + UvElement *element; + /* uv pointer for convenience. Caution, this points to the original UVs! */ + float *uv; + /* general use flag (Used to check if Element is boundary here) */ + char flag; +} UvAdjacencyElement; + +typedef struct UvEdge { + unsigned int uv1; + unsigned int uv2; + /* general use flag (Used to check if edge is boundary here, and propagates to adjacency elements) */ + char flag; +}UvEdge; + +typedef struct UVInitialStrokeElement{ + /* index to unique uv */ + int uv; + + /* strength of brush on initial position */ + float strength; + + /* initial uv position */ + float initial_uv[2]; +}UVInitialStrokeElement; + +typedef struct UVInitialStroke{ + /* Initial Selection,for grab brushes for instance */ + UVInitialStrokeElement *initialSelection; + + /* total initially selected UVs*/ + int totalInitialSelected; + + /* initial mouse coordinates */ + float init_coord[2]; +}UVInitialStroke; + + +/* custom data for uv smoothing brush */ +typedef struct UvSculptData{ + /* Contains the first of each set of coincident uvs. + * These will be used to perform smoothing on and propagate the changes + * to their coincident uvs */ + UvAdjacencyElement *uv; + + /* ...Is what it says */ + int totalUniqueUvs; + + /* Edges used for adjacency info, used with laplacian smoothing */ + UvEdge *uvedges; + + /* Need I say more? */ + int totalUvEdges; + + /* data for initial stroke, used by tools like grab */ + UVInitialStroke *initial_stroke; + + /* Timer to be used for airbrush-type brush */ + wmTimer *timer; + + /* To determine quickly adjacent uvs */ + UvElementMap *elementMap; + + /* uvsmooth Paint for fast reference */ + Paint *uvsculpt; +}UvSculptData; + +/*********** Improved Laplacian Relaxation Operator ************************/ +/* Original code by Raul Fernandez Hernandez "farsthary" * + * adapted to uv smoothing by Antony Riakiatakis * + ***************************************************************************/ + +typedef struct Temp_UvData{ + float sum_co[2], p[2], b[2], sum_b[2]; + int ncounter; +}Temp_UVData; + + + +void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, float mouse_coord[2], float alpha, float radius, float aspectRatio){ + Temp_UVData *tmp_uvdata; + float diff[2]; + int i; + float radius_root = sqrt(radius); + Brush *brush = paint_brush(sculptdata->uvsculpt); + + tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); + + /* counting neighbors */ + for (i = 0; i < sculptdata->totalUvEdges; i++){ + UvEdge *tmpedge = sculptdata->uvedges+i; + tmp_uvdata[tmpedge->uv1].ncounter++; + tmp_uvdata[tmpedge->uv2].ncounter++; + + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + copy_v2_v2(diff,tmp_uvdata[i].sum_co); + mul_v2_fl(diff,1.f/tmp_uvdata[i].ncounter); + copy_v2_v2(tmp_uvdata[i].p,diff); + + tmp_uvdata[i].b[0] = diff[0] - sculptdata->uv[i].uv[0]; + tmp_uvdata[i].b[1] = diff[1] - sculptdata->uv[i].uv[1]; + } + + for (i = 0; i < sculptdata->totalUvEdges; i++){ + UvEdge *tmpedge = sculptdata->uvedges+i; + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_b, tmp_uvdata[tmpedge->uv2].b); + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_b, tmp_uvdata[tmpedge->uv1].b); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + float dist; + /* This is supposed to happen only if "Pin Edges" is on, since we have initialization on stroke start + * If ever uv brushes get their own mode we should check for toolsettings option too */ + if((sculptdata->uv[i].flag & MARK_BOUNDARY)){ + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + UvElement *element; + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + + sculptdata->uv[i].uv[0] = (1.0-strength)*sculptdata->uv[i].uv[0] + strength*(tmp_uvdata[i].p[0] - 0.5f*(tmp_uvdata[i].b[0] + tmp_uvdata[i].sum_b[0]/tmp_uvdata[i].ncounter)); + sculptdata->uv[i].uv[1] = (1.0-strength)*sculptdata->uv[i].uv[1] + strength*(tmp_uvdata[i].p[1] - 0.5f*(tmp_uvdata[i].b[1] + tmp_uvdata[i].sum_b[1]/tmp_uvdata[i].ncounter)); + + for(element = sculptdata->uv[i].element; element; element = element->next){ +#if 0 /* BMESH_TODO */ + MTFace *mt; + if(element->separate && element != sculptdata->uv[i].element) + break; + mt = CustomData_em_get(&em->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[i].uv); +#else + (void)em; +#endif /* BMESH_TODO */ + } + } + } + + MEM_freeN(tmp_uvdata); + + return; +} + +static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, float mouse_coord[2], float alpha, float radius, float aspectRatio) +{ + Temp_UVData *tmp_uvdata; + float diff[2]; + int i; + float radius_root = sqrt(radius); + Brush *brush = paint_brush(sculptdata->uvsculpt); + + tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); + + /* counting neighbors */ + for (i = 0; i < sculptdata->totalUvEdges; i++){ + UvEdge *tmpedge = sculptdata->uvedges+i; + tmp_uvdata[tmpedge->uv1].ncounter++; + tmp_uvdata[tmpedge->uv2].ncounter++; + + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } + + /* Original Lacplacian algorithm included removal of normal component of translation. here it is not + * needed since we translate along the UV plane always.*/ + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co); + mul_v2_fl(tmp_uvdata[i].p, 1.f/tmp_uvdata[i].ncounter); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + float dist; + /* This is supposed to happen only if "Pin Edges" is on, since we have initialization on stroke start + * If ever uv brushes get their own mode we should check for toolsettings option too */ + if((sculptdata->uv[i].flag & MARK_BOUNDARY)){ + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + UvElement *element; + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + + sculptdata->uv[i].uv[0] = (1.0-strength)*sculptdata->uv[i].uv[0] + strength*tmp_uvdata[i].p[0]; + sculptdata->uv[i].uv[1] = (1.0-strength)*sculptdata->uv[i].uv[1] + strength*tmp_uvdata[i].p[1]; + + for(element = sculptdata->uv[i].element; element; element = element->next){ +#if 0 /* BMESH_TODO */ + MTFace *mt; + if(element->separate && element != sculptdata->uv[i].element) + break; + mt = CustomData_em_get(&em->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[i].uv); +#else + (void)em; +#endif /* BMESH_TODO */ + } + } + } + + MEM_freeN(tmp_uvdata); + + return; +} + + +static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, wmEvent *event, Object *obedit) +{ + float co[2], radius, radius_root; + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh; + unsigned int tool; + UvSculptData *sculptdata = (UvSculptData *)op->customdata; + SpaceImage *sima; + int invert; + int width, height; + float aspectRatio; + float alpha, zoomx, zoomy; + Brush *brush = paint_brush(sculptdata->uvsculpt); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + tool = RNA_boolean_get(op->ptr, "temp_relax")? UV_SCULPT_TOOL_RELAX : toolsettings->uv_sculpt_tool; + + invert = RNA_boolean_get(op->ptr, "invert")? -1 : 1; + alpha = brush_alpha(scene, brush); + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + sima = CTX_wm_space_image(C); + ED_space_image_size(sima, &width, &height); + ED_space_image_zoom(sima, ar, &zoomx, &zoomy); + + radius = brush_size(scene, brush)/(width*zoomx); + aspectRatio = width/(float)height; + + /* We will compare squares to save some computation */ + radius = radius*radius; + radius_root = sqrt(radius); + + /* + * Pinch Tool + */ + if(tool == UV_SCULPT_TOOL_PINCH){ + int i; + alpha *= invert; + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + float dist, diff[2]; + /* This is supposed to happen only if "Lock Borders" is on, since we have initialization on stroke start + * If ever uv brushes get their own mode we should check for toolsettings option too */ + if(sculptdata->uv[i].flag & MARK_BOUNDARY){ + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, co); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + UvElement *element; + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + normalize_v2(diff); + + sculptdata->uv[i].uv[0] -= strength*diff[0]*0.001; + sculptdata->uv[i].uv[1] -= strength*diff[1]*0.001; + + for(element = sculptdata->uv[i].element; element; element = element->next){ +#if 0 /* BMESH_TODO*/ + MTFace *mt; + if(element->separate && element != sculptdata->uv[i].element) + break; + mt = CustomData_em_get(&bm->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[i].uv); +#endif + } + } + } + } + + /* + * Smooth Tool + */ + else if(tool == UV_SCULPT_TOOL_RELAX){ + unsigned int method = toolsettings->uv_relax_method; + if(method == UV_SCULPT_TOOL_RELAX_HC){ + HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); + }else{ + laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); + } + } + + /* + * Grab Tool + */ + else if(tool == UV_SCULPT_TOOL_GRAB){ + int i; + float diff[2]; + sub_v2_v2v2(diff, co, sculptdata->initial_stroke->init_coord); + + for(i = 0; i < sculptdata->initial_stroke->totalInitialSelected; i++ ){ + UvElement *element; + int uvindex = sculptdata->initial_stroke->initialSelection[i].uv; + float strength = sculptdata->initial_stroke->initialSelection[i].strength; + sculptdata->uv[uvindex].uv[0] = sculptdata->initial_stroke->initialSelection[i].initial_uv[0] + strength*diff[0]; + sculptdata->uv[uvindex].uv[1] = sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength*diff[1]; + + for(element = sculptdata->uv[uvindex].element; element; element = element->next){ +#if 0 /* BMESH_TODO */ + MTFace *mt; + if(element->separate && element != sculptdata->uv[uvindex].element) + break; + mt = CustomData_em_get(&em->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[uvindex].uv); +#endif /* BMESH_TODO */ + } + } + } +} + + +static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) +{ + UvSculptData *data = op->customdata; + if(data->timer){ + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); + } + if(data->elementMap) + { + EDBM_free_uv_element_map(data->elementMap); + } + if(data->uv){ + MEM_freeN(data->uv); + } + if(data->uvedges){ + MEM_freeN(data->uvedges); + } + if(data->initial_stroke){ + if(data->initial_stroke->initialSelection){ + MEM_freeN(data->initial_stroke->initialSelection); + } + MEM_freeN(data->initial_stroke); + } + + MEM_freeN(data); + op->customdata = NULL; +} + +static int get_uv_element_offset_from_face(UvElementMap *map, BMFace *efa, int index, int island_index, int doIslands){ + UvElement *element = ED_get_uv_element(map, efa, index); + if(!element || (doIslands && element->island != island_index)){ + return -1; + } + return element - map->buf; +} + + +static unsigned int uv_edge_hash(const void *key){ + UvEdge *edge = (UvEdge *)key; + return + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv2)) + + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv1)); +} + +static int uv_edge_compare(const void *a, const void *b){ + UvEdge *edge1 = (UvEdge *)a; + UvEdge *edge2 = (UvEdge *)b; + + if((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)){ + return 0; + } + return 1; +} + + +static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, wmEvent *event) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = scene->toolsettings; + UvSculptData *data = MEM_callocN(sizeof(*data), "UV Smooth Brush Data"); + BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh; + BMesh *bm = em->bm; + + op->customdata = data; + + if(data){ + int counter = 0, i; + ARegion *ar= CTX_wm_region(C); + float co[2]; + EditFace *efa; + UvEdge *edges; + GHash *edgeHash; + GHashIterator* ghi; + MTFace *mt; + int do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); + int island_index = 0; + /* Holds, for each UvElement in elementMap, a pointer to its unique uv.*/ + int *uniqueUv; + + data->uvsculpt = &ts->uvsculpt->paint; + + if(do_island_optimization){ + /* We will need island information */ + if(ts->uv_flag & UV_SYNC_SELECTION){ + data->elementMap = EDBM_make_uv_element_map(em, 0, 1); + }else{ + data->elementMap = EDBM_make_uv_element_map(em, 1, 1); + } + }else { + if(ts->uv_flag & UV_SYNC_SELECTION){ + data->elementMap = EDBM_make_uv_element_map(em, 0, 0); + }else{ + data->elementMap = EDBM_make_uv_element_map(em, 1, 0); + } + } + + if(!data->elementMap){ + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + /* Mouse coordinates, useful for some functions like grab and sculpt all islands */ + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + /* we need to find the active island here */ + if(do_island_optimization){ + UvElement *element; + NearestHit hit; + Image *ima= CTX_data_edit_image(C); + uv_find_nearest_vert(scene, ima, em, co, NULL, &hit); + + element = ED_get_uv_element(data->elementMap, hit.efa, hit.lindex); + island_index = element->island; + } + + + /* Count 'unique' uvs */ + for(i = 0; i < data->elementMap->totalUVs; i++){ + if(data->elementMap->buf[i].separate + && (!do_island_optimization || data->elementMap->buf[i].island == island_index)){ + counter++; + } + } + + /* Allocate the unique uv buffers */ + data->uv = MEM_mallocN(sizeof(*data->uv)*counter, "uv_brush_unique_uvs"); + uniqueUv = MEM_mallocN(sizeof(*uniqueUv)*data->elementMap->totalUVs, "uv_brush_unique_uv_map"); + edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); + /* we have at most totalUVs edges */ + edges = MEM_mallocN(sizeof(*edges)*data->elementMap->totalUVs, "uv_brush_all_edges"); + if(!data->uv || !uniqueUv || !edgeHash || !edges){ + if(edges){ + MEM_freeN(edges); + } + if(uniqueUv){ + MEM_freeN(uniqueUv); + } + if(edgeHash){ + MEM_freeN(edgeHash); + } + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + data->totalUniqueUvs = counter; + /* So that we can use this as index for the UvElements */ + counter = -1; + /* initialize the unique UVs */ + for(i = 0; i < bm->totvert; i++){ + UvElement *element = data->elementMap->vert[i]; + for(; element; element = element->next){ + if(element->separate){ + if(do_island_optimization && (element->island != island_index)){ + /* skip this uv if not on the active island */ + for(; element->next && !(element->next->separate); element = element->next) + ; + continue; + } +#if 0 /* BMESH_TODO */ + efa = element->face; + mt = CustomData_em_get(&bm->fdata, efa->data, CD_MTFACE); + + counter++; + data->uv[counter].element = element; + data->uv[counter].flag = 0; + data->uv[counter].uv = mt->uv[element->tfindex]; +#else + (void)efa; + (void)mt; +#endif /* BMESH_TODO */ + } + /* pointer arithmetic to the rescue, as always :)*/ + uniqueUv[element - data->elementMap->buf] = counter; + } + } + +#if 0 /* BMESH_TODO */ + /* Now, on to generate our uv connectivity data */ + for(efa = em->faces.first, counter = 0; efa; efa = efa->next){ + int nverts = efa->v4 ? 4 : 3; + for(i = 0; i < nverts; i++){ + int offset1, itmp1 = get_uv_element_offset_from_face(data->elementMap, efa, i, island_index, do_island_optimization); + int offset2, itmp2 = get_uv_element_offset_from_face(data->elementMap, efa, (i+1)%nverts, island_index, do_island_optimization); + + /* Skip edge if not found(unlikely) or not on valid island */ + if(itmp1 == -1 || itmp2 == -1) + continue; + + offset1 = uniqueUv[itmp1]; + offset2 = uniqueUv[itmp2]; + + edges[counter].flag = 0; + /* using an order policy, sort uvs according to address space. This avoids + * Having two different UvEdges with the same uvs on different positions */ + if(offset1 < offset2){ + edges[counter].uv1 = offset1; + edges[counter].uv2 = offset2; + } + else{ + edges[counter].uv1 = offset2; + edges[counter].uv2 = offset1; + } + /* Hack! Set the value of the key to its flag. Now we can set the flag when an edge exists twice :) */ + if(BLI_ghash_haskey(edgeHash, &edges[counter])){ + char *flag = BLI_ghash_lookup(edgeHash, &edges[counter]); + *flag = 1; + } + else{ + /* Hack mentioned */ + BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag); + } + counter++; + } + } +#endif /* BMESH_TODO */ + + MEM_freeN(uniqueUv); + + /* Allocate connectivity data, we allocate edges once */ + data->uvedges = MEM_mallocN(sizeof(*data->uvedges)*BLI_ghash_size(edgeHash), "uv_brush_edge_connectivity_data"); + if(!data->uvedges){ + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + uv_sculpt_stroke_exit(C, op); + return NULL; + } + ghi = BLI_ghashIterator_new(edgeHash); + if(!ghi){ + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + uv_sculpt_stroke_exit(C, op); + return NULL; + } + /* fill the edges with data */ + for(i = 0; !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){ + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi)); + } + data->totalUvEdges = BLI_ghash_size(edgeHash); + + /* cleanup temporary stuff */ + BLI_ghashIterator_free(ghi); + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + + /* transfer boundary edge property to uvs */ + if(ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS){ + for(i = 0; i < data->totalUvEdges; i++){ + if(!data->uvedges[i].flag){ + data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; + data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; + } + } + } + + /* Allocate initial selection for grab tool */ + if(ts->uv_sculpt_tool == UV_SCULPT_TOOL_GRAB){ + float radius, radius_root; + UvSculptData *sculptdata = (UvSculptData *)op->customdata; + SpaceImage *sima; + int width, height; + float aspectRatio; + float alpha, zoomx, zoomy; + Brush *brush = paint_brush(sculptdata->uvsculpt); + + alpha = brush_alpha(scene, brush); + + radius = brush_size(scene, brush); + sima = CTX_wm_space_image(C); + ED_space_image_size(sima, &width, &height); + ED_space_image_zoom(sima, ar, &zoomx, &zoomy); + + aspectRatio = width/(float)height; + radius /= (width*zoomx); + radius = radius*radius; + radius_root = sqrt(radius); + + /* Allocate selection stack */ + data->initial_stroke = MEM_mallocN(sizeof(*data->initial_stroke), "uv_sculpt_initial_stroke"); + if(!data->initial_stroke){ + uv_sculpt_stroke_exit(C, op); + } + data->initial_stroke->initialSelection = MEM_mallocN(sizeof(*data->initial_stroke->initialSelection)*data->totalUniqueUvs, "uv_sculpt_initial_selection"); + if(!data->initial_stroke->initialSelection){ + uv_sculpt_stroke_exit(C, op); + } + + copy_v2_v2(data->initial_stroke->init_coord, co); + + counter = 0; + + for(i = 0; i < data->totalUniqueUvs; i++){ + float dist, diff[2]; + if(data->uv[i].flag & MARK_BOUNDARY){ + continue; + } + + sub_v2_v2v2(diff, data->uv[i].uv, co); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + + data->initial_stroke->initialSelection[counter].uv = i; + data->initial_stroke->initialSelection[counter].strength = strength; + copy_v2_v2(data->initial_stroke->initialSelection[counter].initial_uv, data->uv[i].uv); + counter++; + } + } + + data->initial_stroke->totalInitialSelected = counter; + } + } + + return op->customdata; +} + +static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + UvSculptData *data; + Object *obedit = CTX_data_edit_object(C); + + if(!(data = uv_sculpt_stroke_init(C, op, event))) { + return OPERATOR_CANCELLED; + } + + uv_sculpt_stroke_apply(C, op, event, obedit); + + data->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.001f); + + if(!data->timer){ + uv_sculpt_stroke_exit(C, op); + return OPERATOR_CANCELLED; + } + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + + +static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + UvSculptData *data = (UvSculptData *)op->customdata; + Object *obedit = CTX_data_edit_object(C); + + switch(event->type) { + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: + uv_sculpt_stroke_exit(C, op); + return OPERATOR_FINISHED; + + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + uv_sculpt_stroke_apply(C, op, event, obedit); + break; + case TIMER: + if(event->customdata == data->timer) + uv_sculpt_stroke_apply(C, op, event, obedit); + break; + default: + return OPERATOR_RUNNING_MODAL; + } + + ED_region_tag_redraw(CTX_wm_region(C)); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + DAG_id_tag_update(obedit->data, 0); + return OPERATOR_RUNNING_MODAL; +} + +void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sculpt UVs"; + ot->description = "Sculpt UVs using a brush"; + ot->idname = "SCULPT_OT_uv_sculpt_stroke"; + + /* api callbacks */ + ot->invoke = uv_sculpt_stroke_invoke; + ot->modal = uv_sculpt_stroke_modal; + ot->poll = uv_sculpt_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "invert", 0, "Invert", "Inverts the operator"); + RNA_def_boolean(ot->srna, "temp_relax", 0, "Relax", "Relax Tool"); +} |