Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-01-18 01:08:25 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-01-18 01:08:25 +0400
commit0f28c1c27aec41dd22e31aac2c02cdfae785dd1b (patch)
tree1c41b37d29a56390d810846cd7fe8f1218e40576 /source/blender/editors
parent9be40c026df54fee796c4073ebd4734c55ed5807 (diff)
parent408f7963c5cfd65792d826390b01896282de8e97 (diff)
svn merge ^/trunk/blender -r43461:43472
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/include/ED_image.h1
-rw-r--r--source/blender/editors/include/ED_mesh.h6
-rw-r--r--source/blender/editors/include/ED_uvedit.h2
-rw-r--r--source/blender/editors/include/ED_view3d.h4
-rw-r--r--source/blender/editors/include/UI_resources.h10
-rw-r--r--source/blender/editors/interface/resources.c40
-rw-r--r--source/blender/editors/mesh/bmeshutils.c233
-rw-r--r--source/blender/editors/object/object_edit.c6
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt3
-rw-r--r--source/blender/editors/sculpt_paint/SConscript2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c110
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h4
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c55
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c787
-rw-r--r--source/blender/editors/space_image/image_ops.c28
-rw-r--r--source/blender/editors/space_image/space_image.c6
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt1
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c117
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h58
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c277
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c18
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c1446
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c240
23 files changed, 3385 insertions, 69 deletions
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index 957b58b141c..2058479bed8 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -53,6 +53,7 @@ void ED_space_image_zoom(struct SpaceImage *sima, struct ARegion *ar, float *zoo
void ED_space_image_uv_aspect(struct SpaceImage *sima, float *aspx, float *aspy);
void ED_space_image_paint_update(struct wmWindowManager *wm, struct ToolSettings *settings);
+void ED_space_image_uv_sculpt_update(struct wmWindowManager *wm, struct ToolSettings *settings);
void ED_image_size(struct Image *ima, int *width, int *height);
void ED_image_aspect(struct Image *ima, float *aspx, float *aspy);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index e9884c5d8ce..4abba91b716 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -182,6 +182,12 @@ int EDBM_init_backbuf_circle(struct ViewContext *vc, short xs, short ys, short
void EDBM_deselect_by_material(struct BMEditMesh *em, const short index, const short select);
+struct UvElementMap *EDBM_make_uv_element_map(struct BMEditMesh *em, int selected, int doIslands);
+void EDBM_free_uv_element_map(struct UvElementMap *vmap);
+
+void EDBM_add_data_layer(struct BMEditMesh *em, struct CustomData *data, int type, const char *name);
+void EDBM_free_data_layer(struct BMEditMesh *em, struct CustomData *data, int type);
+
void EDBM_select_swap(struct BMEditMesh *em); /* exported for UV */
int EDBM_texFaceCheck(struct BMEditMesh *em);
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 2f193292eec..c19872213ca 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -84,7 +84,7 @@ void ED_uvedit_live_unwrap_end(short cancel);
void ED_unwrap_lscm(struct Scene *scene, struct Object *obedit, const short sel);
/* uvedit_draw.c */
-void draw_uvedit_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene, struct Object *obedit);
+void draw_uvedit_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene, struct Object *obedit, struct Object *obact);
/* uvedit_buttons.c */
void ED_uvedit_buttons_register(struct ARegionType *art);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 362fc9fcef9..29585d1c5a3 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -283,10 +283,10 @@ void ED_view3d_draw_offscreen(struct Scene *scene, struct View3D *v3d, struct AR
int winx, int winy, float viewmat[][4], float winmat[][4]);
struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, char err_out[256]);
-struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, char err_out[256]);
+struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, char err_out[256]);
-Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
+struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, short do_clip);
int ED_view3d_lock(struct RegionView3D *rv3d);
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 5d95992f7d4..852b030c0d2 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -255,7 +255,15 @@ enum {
TH_PATH_BEFORE,
TH_PATH_AFTER,
TH_CAMERA_PATH,
- TH_LOCK_MARKER
+ TH_LOCK_MARKER,
+
+ TH_STITCH_PREVIEW_FACE,
+ TH_STITCH_PREVIEW_EDGE,
+ TH_STITCH_PREVIEW_VERT,
+ TH_STITCH_PREVIEW_STITCHABLE,
+ TH_STITCH_PREVIEW_UNSTITCHABLE,
+ TH_STITCH_PREVIEW_ACTIVE
+
};
/* XXX WARNING: previous is saved in file, so do not change order! */
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index c3fe50edcd3..ad20ff0b6dd 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -410,6 +410,28 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo
cp= ts->preview_back;
break;
+ case TH_STITCH_PREVIEW_FACE:
+ cp = ts->preview_stitch_face;
+ break;
+
+ case TH_STITCH_PREVIEW_EDGE:
+ cp = ts->preview_stitch_edge;
+ break;
+
+ case TH_STITCH_PREVIEW_VERT:
+ cp = ts->preview_stitch_vert;
+ break;
+
+ case TH_STITCH_PREVIEW_STITCHABLE:
+ cp = ts->preview_stitch_stitchable;
+ break;
+
+ case TH_STITCH_PREVIEW_UNSTITCHABLE:
+ cp = ts->preview_stitch_unstitchable;
+ break;
+ case TH_STITCH_PREVIEW_ACTIVE:
+ cp = ts->preview_stitch_active;
+ break;
case TH_MARKER_OUTLINE:
cp= ts->marker_outline; break;
case TH_MARKER:
@@ -746,6 +768,11 @@ void ui_theme_init_default(void)
SETCOL(btheme->tima.face_select, 255, 133, 0, 60);
SETCOL(btheme->tima.editmesh_active, 255, 255, 255, 128);
SETCOLF(btheme->tima.preview_back, 0.45, 0.45, 0.45, 1.0);
+ SETCOLF(btheme->tima.preview_stitch_face, 0.5, 0.5, 0.0, 0.2);
+ SETCOLF(btheme->tima.preview_stitch_edge, 1.0, 0.0, 1.0, 0.2);
+ SETCOLF(btheme->tima.preview_stitch_vert, 0.0, 0.0, 1.0, 0.2);
+ SETCOLF(btheme->tima.preview_stitch_stitchable, 0.0, 1.0, 0.0, 1.0);
+ SETCOLF(btheme->tima.preview_stitch_unstitchable, 1.0, 0.0, 0.0, 1.0);
/* space text */
btheme->text= btheme->tv3d;
@@ -1679,6 +1706,19 @@ void init_userdef_do_versions(void)
}
}
+ if (bmain->versionfile < 262){
+ bTheme *btheme;
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ SETCOLF(btheme->tima.preview_stitch_face, 0.071, 0.259, 0.694, 0.150);
+ SETCOLF(btheme->tima.preview_stitch_edge, 1.0, 0.522, 0.0, 0.7);
+ SETCOLF(btheme->tima.preview_stitch_vert, 1.0, 0.522, 0.0, 0.5);
+ SETCOLF(btheme->tima.preview_stitch_stitchable, 0.0, 1.0, 0.0, 1.0);
+ SETCOLF(btheme->tima.preview_stitch_unstitchable, 1.0, 0.0, 0.0, 1.0);
+ SETCOLF(btheme->tima.preview_stitch_active, 0.886, 0.824, 0.765, 0.140);
+ }
+ U.use_16bit_textures = 0;
+ }
+
/* GL Texture Garbage Collection (variable abused above!) */
if (U.textimeout == 0) {
U.texcollectrate = 60;
diff --git a/source/blender/editors/mesh/bmeshutils.c b/source/blender/editors/mesh/bmeshutils.c
index 6defe21e6c2..1d3851b2650 100644
--- a/source/blender/editors/mesh/bmeshutils.c
+++ b/source/blender/editors/mesh/bmeshutils.c
@@ -752,6 +752,230 @@ UvMapVert *EDBM_get_uv_map_vert(UvVertMap *vmap, unsigned int v)
return vmap->vert[v];
}
+/* from editmesh_lib.c in trunk */
+#if 0 /* BMESH_TODO */
+
+/* A specialized vert map used by stitch operator */
+UvElementMap *EDBM_make_uv_element_map(EditMesh *em, int selected, int do_islands)
+{
+ EditVert *ev;
+ EditFace *efa;
+
+ /* vars from original func */
+ UvElementMap *vmap;
+ UvElement *buf;
+ UvElement *islandbuf;
+ MTFace *tf;
+ unsigned int a;
+ int i,j, totuv, nverts, nislands = 0, islandbufsize = 0;
+ unsigned int *map;
+ /* for uv island creation */
+ EditFace **stack;
+ int stacksize = 0;
+
+ /* we need the vert */
+ for(ev = em->verts.first, i = 0; ev; ev = ev->next, i++)
+ ev->tmp.l = i;
+
+ totuv = 0;
+
+ for(efa = em->faces.first; efa; efa = efa->next)
+ if(!selected || ((!efa->h) && (efa->f & SELECT)))
+ totuv += (efa->v4)? 4: 3;
+
+ if(totuv == 0)
+ return NULL;
+
+ vmap = (UvElementMap *)MEM_callocN(sizeof(*vmap), "UvVertElementMap");
+ if(!vmap)
+ return NULL;
+
+ vmap->vert = (UvElement**)MEM_callocN(sizeof(*vmap->vert)*em->totvert, "UvElementVerts");
+ buf = vmap->buf = (UvElement*)MEM_callocN(sizeof(*vmap->buf)*totuv, "UvElement");
+
+ if(!vmap->vert || !vmap->buf) {
+ EDBM_free_uv_element_map(vmap);
+ return NULL;
+ }
+
+ vmap->totalUVs = totuv;
+
+ for(efa = em->faces.first; efa; a++, efa = efa->next) {
+ if(!selected || ((!efa->h) && (efa->f & SELECT))) {
+ nverts = (efa->v4)? 4: 3;
+
+ for(i = 0; i<nverts; i++) {
+ buf->tfindex = i;
+ buf->face = efa;
+ buf->separate = 0;
+ buf->island = INVALID_ISLAND;
+
+ buf->next = vmap->vert[(*(&efa->v1 + i))->tmp.l];
+ vmap->vert[(*(&efa->v1 + i))->tmp.l] = buf;
+
+ buf++;
+ }
+ }
+
+ efa->tmp.l = INVALID_ISLAND;
+ }
+
+ /* sort individual uvs for each vert */
+ for(a = 0, ev = em->verts.first; ev; a++, ev = ev->next) {
+ UvElement *newvlist = NULL, *vlist = vmap->vert[a];
+ UvElement *iterv, *v, *lastv, *next;
+ float *uv, *uv2;
+
+ while(vlist) {
+ v= vlist;
+ vlist= vlist->next;
+ v->next= newvlist;
+ newvlist= v;
+
+ efa = v->face;
+ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ uv = tf->uv[v->tfindex];
+
+ lastv= NULL;
+ iterv= vlist;
+
+ while(iterv) {
+ next= iterv->next;
+ efa = iterv->face;
+ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ uv2 = tf->uv[iterv->tfindex];
+
+ if(fabsf(uv[0]-uv2[0]) < STD_UV_CONNECT_LIMIT && fabsf(uv[1]-uv2[1]) < STD_UV_CONNECT_LIMIT) {
+ if(lastv) lastv->next = next;
+ else vlist = next;
+ iterv->next = newvlist;
+ newvlist = iterv;
+ }
+ else
+ lastv = iterv;
+
+ iterv = next;
+ }
+
+ newvlist->separate = 1;
+ }
+
+ vmap->vert[a] = newvlist;
+ }
+
+ if(do_islands) {
+ /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. Now we should sort uv's in islands. */
+
+ /* map holds the map from current vmap->buf to the new, sorted map*/
+ map = MEM_mallocN(sizeof(*map)*totuv, "uvelement_remap");
+ stack = MEM_mallocN(sizeof(*stack)*em->totface, "uv_island_face_stack");
+ islandbuf = MEM_callocN(sizeof(*islandbuf)*totuv, "uvelement_island_buffer");
+
+ for(i = 0; i < totuv; i++) {
+ if(vmap->buf[i].island == INVALID_ISLAND) {
+ vmap->buf[i].island = nislands;
+ stack[0] = vmap->buf[i].face;
+ stack[0]->tmp.l = nislands;
+ stacksize=1;
+
+ while(stacksize > 0) {
+ efa = stack[--stacksize];
+ nverts = efa->v4? 4 : 3;
+
+ for(j = 0; j < nverts; j++) {
+ UvElement *element, *initelement = vmap->vert[(*(&efa->v1 + j))->tmp.l];
+
+ for(element = initelement; element; element = element->next) {
+ if(element->separate)
+ initelement = element;
+
+ if(element->face == efa) {
+ /* found the uv corresponding to our face and vertex. Now fill it to the buffer */
+ element->island = nislands;
+ map[element - vmap->buf] = islandbufsize;
+ islandbuf[islandbufsize].tfindex = element->tfindex;
+ islandbuf[islandbufsize].face = element->face;
+ islandbuf[islandbufsize].separate = element->separate;
+ islandbuf[islandbufsize].island = nislands;
+ islandbufsize++;
+
+ for(element = initelement; element; element = element->next) {
+ if(element->separate && element != initelement)
+ break;
+
+ if(element->face->tmp.l == INVALID_ISLAND) {
+ stack[stacksize++] = element->face;
+ element->face->tmp.l = nislands;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ nislands++;
+ }
+ }
+
+ /* remap */
+ for(i = 0; i < em->totvert; i++) {
+ /* important since we may do selection only. Some of these may be NULL */
+ if(vmap->vert[i])
+ vmap->vert[i] = &islandbuf[map[vmap->vert[i] - vmap->buf]];
+ }
+
+ vmap->islandIndices = MEM_callocN(sizeof(*vmap->islandIndices)*nislands,"UvVertMap2_island_indices");
+ if(!vmap->islandIndices) {
+ MEM_freeN(islandbuf);
+ MEM_freeN(stack);
+ MEM_freeN(map);
+ EDBM_free_uv_element_map(vmap);
+ }
+
+ j = 0;
+ for(i = 0; i < totuv; i++) {
+ UvElement *element = vmap->buf[i].next;
+ if(element == NULL)
+ islandbuf[map[i]].next = NULL;
+ else
+ islandbuf[map[i]].next = &islandbuf[map[element - vmap->buf]];
+
+ if(islandbuf[i].island != j) {
+ j++;
+ vmap->islandIndices[j] = i;
+ }
+ }
+
+ MEM_freeN(vmap->buf);
+
+ vmap->buf = islandbuf;
+ vmap->totalIslands = nislands;
+ MEM_freeN(stack);
+ MEM_freeN(map);
+ }
+
+ return vmap;
+}
+
+#else
+
+UvElementMap *EDBM_make_uv_element_map(BMEditMesh *em, int selected, int do_islands)
+{
+ (void)em;
+ (void)selected;
+ (void)do_islands;
+
+ return NULL;
+}
+
+#endif /* BMESH_TODO */
+
+UvMapVert *EM_get_uv_map_vert(UvVertMap *vmap, unsigned int v)
+{
+ return vmap->vert[v];
+}
+
void EDBM_free_uv_vert_map(UvVertMap *vmap)
{
if (vmap) {
@@ -761,6 +985,15 @@ void EDBM_free_uv_vert_map(UvVertMap *vmap)
}
}
+void EDBM_free_uv_element_map(UvElementMap *vmap)
+{
+ if (vmap) {
+ if (vmap->vert) MEM_freeN(vmap->vert);
+ if (vmap->buf) MEM_freeN(vmap->buf);
+ if (vmap->islandIndices) MEM_freeN(vmap->islandIndices);
+ MEM_freeN(vmap);
+ }
+}
/* last_sel, use em->act_face otherwise get the last selected face in the editselections
* at the moment, last_sel is mainly useful for gaking sure the space image dosnt flicker */
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 5f313301e2e..a7ac91d13df 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -89,7 +89,7 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_util.h"
-
+#include "ED_image.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -514,11 +514,15 @@ void ED_object_enter_editmode(bContext *C, int flag)
static int editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
+ ToolSettings *toolsettings = CTX_data_tool_settings(C);
+
if(!CTX_data_edit_object(C))
ED_object_enter_editmode(C, EM_WAITCURSOR);
else
ED_object_exit_editmode(C, EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR); /* had EM_DO_UNDO but op flag calls undo too [#24685] */
+ ED_space_image_uv_sculpt_update(CTX_wm_manager(C), toolsettings);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 30f49264e90..29e97c77bec 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -23,10 +23,12 @@ set(INC
../../blenkernel
../../blenlib
../../blenloader
+ ../../bmesh
../../gpu
../../imbuf
../../makesdna
../../makesrna
+ ../uvedit
../../render/extern/include
../../windowmanager
../../../../intern/guardedalloc
@@ -45,6 +47,7 @@ set(SRC
paint_vertex.c
sculpt.c
sculpt_undo.c
+ sculpt_uv.c
paint_intern.h
sculpt_intern.h
diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript
index 8f1f47b5004..8669ea6c695 100644
--- a/source/blender/editors/sculpt_paint/SConscript
+++ b/source/blender/editors/sculpt_paint/SConscript
@@ -8,7 +8,7 @@ defs = []
incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
incs += ' ../../render/extern/include'
-incs += ' ../../gpu ../../makesrna ../../blenloader ../../bmesh'
+incs += ' ../../gpu ../../makesrna ../../blenloader ../../bmesh ../uvedit'
if env['OURPLATFORM'] == 'linux':
cflags='-pthread'
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 5f4777331c3..1e989cce14a 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -50,6 +50,7 @@
#include "BLI_memarena.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_editVert.h"
#include "PIL_time.h"
@@ -80,6 +81,10 @@
#include "BKE_paint.h"
#include "BKE_report.h"
#include "BKE_scene.h"
+#include "BKE_global.h"
+#include "BKE_deform.h"
+
+#include "BKE_tessmesh.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
@@ -91,6 +96,7 @@
#include "ED_sculpt.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
+#include "ED_mesh.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -100,6 +106,7 @@
#include "RNA_enum_types.h"
#include "GPU_draw.h"
+#include "GPU_extensions.h"
#include "paint_intern.h"
@@ -3742,15 +3749,13 @@ static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, floa
static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels_f, float co[2])
{
- unsigned char rgba_ub[4];
- unsigned char rgba_smear[4];
+ float rgba[4];
- if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1)==0)
+ if (project_paint_PickColor(ps, co, rgba, NULL, 1)==0)
return;
- IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba_smear, projPixel->pixel.f_pt);
/* (ProjPixelClone *)projPixel)->clonepx.uint = IMB_blend_color(*((unsigned int *)rgba_smear), *((unsigned int *)rgba_ub), (int)(alpha*mask*255), ps->blend); */
- blend_color_mix(((ProjPixelClone *)projPixel)->clonepx.ch, rgba_smear, (rgba_ub), (int)(alpha*mask*255));
+ blend_color_mix_float(((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, alpha*mask);
BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena);
}
@@ -3782,8 +3787,8 @@ static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, flo
{
if (ps->is_texbrush) {
/* rgba already holds a texture result here from higher level function */
- float rgba_br[3];
if(use_color_correction){
+ float rgba_br[3];
srgb_to_linearrgb_v3_v3(rgba_br, ps->brush->rgb);
mul_v3_v3(rgba, rgba_br);
}
@@ -3999,7 +4004,7 @@ static void *do_projectpaint_thread(void *ph_v)
for (node= smearPixels_f; node; node= node->next) {
projPixel = node->link;
- IMAPAINT_CHAR_RGBA_TO_FLOAT(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.ch);
+ copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f);
}
BLI_memarena_free(smearArena);
@@ -4167,7 +4172,8 @@ static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, s
if(texpaint || (sima && sima->lock)) {
int w = imapaintpartial.x2 - imapaintpartial.x1;
int h = imapaintpartial.y2 - imapaintpartial.y1;
- GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h, !texpaint);
+ /* Testing with partial update in uv editor too */
+ GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h, 0);//!texpaint);
}
}
@@ -4606,6 +4612,16 @@ static Brush *image_paint_brush(bContext *C)
return paint_brush(&settings->imapaint.paint);
}
+static Brush *uv_sculpt_brush(bContext *C)
+{
+ Scene *scene= CTX_data_scene(C);
+ ToolSettings *settings= scene->toolsettings;
+
+ if(!settings->uvsculpt)
+ return NULL;
+ return paint_brush(&settings->uvsculpt->paint);
+}
+
static int image_paint_poll(bContext *C)
{
Object *obact = CTX_data_active_object(C);
@@ -4630,6 +4646,30 @@ static int image_paint_poll(bContext *C)
return 0;
}
+static int uv_sculpt_brush_poll(bContext *C)
+{
+ BMEditMesh *em;
+ int ret;
+ Object *obedit = CTX_data_edit_object(C);
+ SpaceImage *sima= CTX_wm_space_image(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *toolsettings = scene->toolsettings;
+
+ if(!uv_sculpt_brush(C) || !obedit || obedit->type != OB_MESH)
+ return 0;
+
+ em = ((Mesh *)obedit->data)->edit_btmesh;
+ ret = EDBM_texFaceCheck(em);
+
+ if(ret && sima) {
+ ARegion *ar= CTX_wm_region(C);
+ if((toolsettings->use_uv_sculpt) && ar->regiontype==RGN_TYPE_WINDOW)
+ return 1;
+ }
+
+ return 0;
+}
+
static int image_paint_3d_poll(bContext *C)
{
if(CTX_wm_region_view3d(C))
@@ -5086,7 +5126,7 @@ void PAINT_OT_image_paint(wmOperatorType *ot)
RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
}
-static int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
+int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy)
{
RegionView3D *rv3d= CTX_wm_region_view3d(C);
@@ -5112,16 +5152,27 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)
#define PX_SIZE_FADE_MIN 4.0f
Scene *scene= CTX_data_scene(C);
- Brush *brush= image_paint_brush(C);
+ //Brush *brush= image_paint_brush(C);
Paint *paint= paint_get_active(scene);
+ Brush *brush= paint_brush(paint);
if(paint && brush && paint->flags & PAINT_SHOW_BRUSH) {
+ ToolSettings *ts;
float zoomx, zoomy;
const float size= (float)brush_size(scene, brush);
const short use_zoom= get_imapaint_zoom(C, &zoomx, &zoomy);
- const float pixel_size= MAX2(size * zoomx, size * zoomy);
+ float pixel_size;
float alpha= 0.5f;
+ ts = scene->toolsettings;
+
+ if(use_zoom && !ts->use_uv_sculpt){
+ pixel_size = MAX2(size * zoomx, size * zoomy);
+ }
+ else {
+ pixel_size = size;
+ }
+
/* fade out the brush (cheap trick to work around brush interfearing with sampling [#])*/
if(pixel_size < PX_SIZE_FADE_MIN) {
return;
@@ -5134,7 +5185,8 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)
glTranslatef((float)x, (float)y, 0.0f);
- if(use_zoom)
+ /* No need to scale for uv sculpting, on the contrary it might be useful to keep unscaled */
+ if(use_zoom && !ts->use_uv_sculpt)
glScalef(zoomx, zoomy, 1.0f);
glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], alpha);
@@ -5152,14 +5204,16 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)
static void toggle_paint_cursor(bContext *C, int enable)
{
- ToolSettings *settings= CTX_data_scene(C)->toolsettings;
+ wmWindowManager *wm= CTX_wm_manager(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *settings= scene->toolsettings;
if(settings->imapaint.paintcursor && !enable) {
- WM_paint_cursor_end(CTX_wm_manager(C), settings->imapaint.paintcursor);
+ WM_paint_cursor_end(wm, settings->imapaint.paintcursor);
settings->imapaint.paintcursor = NULL;
}
else if(enable)
- settings->imapaint.paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, brush_drawcursor, NULL);
+ settings->imapaint.paintcursor= WM_paint_cursor_activate(wm, image_paint_poll, brush_drawcursor, NULL);
}
/* enable the paint cursor if it isn't already.
@@ -5178,6 +5232,27 @@ void ED_space_image_paint_update(wmWindowManager *wm, ToolSettings *settings)
}
}
+
+void ED_space_image_uv_sculpt_update(wmWindowManager *wm, ToolSettings *settings)
+{
+ if(settings->use_uv_sculpt) {
+ if(!settings->uvsculpt) {
+ settings->uvsculpt = MEM_callocN(sizeof(*settings->uvsculpt), "UV Smooth paint");
+ settings->uv_sculpt_tool = UV_SCULPT_TOOL_GRAB;
+ settings->uv_sculpt_settings = UV_SCULPT_LOCK_BORDERS | UV_SCULPT_ALL_ISLANDS;
+ settings->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN;
+ }
+
+ paint_init(&settings->uvsculpt->paint, PAINT_CURSOR_SCULPT);
+
+ WM_paint_cursor_activate(wm, uv_sculpt_brush_poll,
+ brush_drawcursor, NULL);
+ }
+ else {
+ if(settings->uvsculpt)
+ settings->uvsculpt->paint.flags &= ~PAINT_SHOW_BRUSH;
+ }
+}
/************************ grab clone operator ************************/
typedef struct GrabClone {
@@ -5499,6 +5574,11 @@ int image_texture_paint_poll(bContext *C)
return (texture_paint_poll(C) || image_paint_poll(C));
}
+int uv_sculpt_poll(bContext *C)
+{
+ return uv_sculpt_brush_poll(C);
+}
+
int facemask_paint_poll(bContext *C)
{
return paint_facesel_test(CTX_data_active_object(C));
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 2fc7d569d63..4ca05e1fbd7 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -103,6 +103,10 @@ void PAINT_OT_texture_paint_toggle(struct wmOperatorType *ot);
void PAINT_OT_project_image(struct wmOperatorType *ot);
void PAINT_OT_image_from_view(struct wmOperatorType *ot);
+/* uv sculpting */
+int uv_sculpt_poll(struct bContext *C);
+
+void SCULPT_OT_uv_sculpt_stroke(struct wmOperatorType *ot);
/* paint_utils.c */
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index d4578e17b3d..0ff0f278f17 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -340,6 +340,39 @@ static void BRUSH_OT_image_tool_set(wmOperatorType *ot)
}
+static int brush_uv_sculpt_tool_set_exec(bContext *C, wmOperator *op)
+{
+ Brush *brush;
+ Scene *scene= CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ ts->uv_sculpt_tool = RNA_enum_get(op->ptr, "tool");
+ brush = ts->uvsculpt->paint.brush;
+ /* To update toolshelf */
+ WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush);
+
+ return OPERATOR_FINISHED;
+}
+
+static void BRUSH_OT_uv_sculpt_tool_set(wmOperatorType *ot)
+{
+ /* from rna_scene.c */
+ extern EnumPropertyItem uv_sculpt_tool_items[];
+ /* identifiers */
+ ot->name = "UV Sculpt Tool Set";
+ ot->description = "Set the uv sculpt tool";
+ ot->idname = "BRUSH_OT_uv_sculpt_tool_set";
+
+ /* api callbacks */
+ ot->exec = brush_uv_sculpt_tool_set_exec;
+ ot->poll = uv_sculpt_poll;
+
+ /* flags */
+ ot->flag = 0;
+
+ /* props */
+ ot->prop = RNA_def_enum(ot->srna, "tool", uv_sculpt_tool_items, 0, "Tool", "");
+}
+
/**************************** registration **********************************/
void ED_operatortypes_paint(void)
@@ -355,6 +388,7 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(BRUSH_OT_vertex_tool_set);
WM_operatortype_append(BRUSH_OT_weight_tool_set);
WM_operatortype_append(BRUSH_OT_image_tool_set);
+ WM_operatortype_append(BRUSH_OT_uv_sculpt_tool_set);
/* image */
WM_operatortype_append(PAINT_OT_texture_paint_toggle);
@@ -373,6 +407,9 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_weight_sample);
WM_operatortype_append(PAINT_OT_weight_sample_group);
+ /* uv */
+ WM_operatortype_append(SCULPT_OT_uv_sculpt_stroke);
+
/* vertex selection */
WM_operatortype_append(PAINT_OT_vert_select_all);
WM_operatortype_append(PAINT_OT_vert_select_inverse);
@@ -619,4 +656,22 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "PAINT_OT_face_select_linked", LKEY, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "PAINT_OT_face_select_linked_pick", LKEY, KM_PRESS, 0, 0);
+
+ keymap= WM_keymap_find(keyconf, "UV Sculpt", 0, 0);
+ keymap->poll= uv_sculpt_poll;
+
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt");
+
+ WM_keymap_add_item(keymap, "SCULPT_OT_uv_sculpt_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_uv_sculpt_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_uv_sculpt_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "temp_relax", 1);
+
+ ed_keymap_paint_brush_size(keymap, "tool_settings.uv_sculpt.brush.size");
+ ed_keymap_paint_brush_radial_control(keymap, "uv_sculpt", 0);
+
+ RNA_enum_set(WM_keymap_add_item(keymap, "BRUSH_OT_uv_sculpt_tool_set", SKEY, KM_PRESS, 0, 0)->ptr, "tool", UV_SCULPT_TOOL_RELAX);
+ RNA_enum_set(WM_keymap_add_item(keymap, "BRUSH_OT_uv_sculpt_tool_set", PKEY, KM_PRESS, 0, 0)->ptr, "tool", UV_SCULPT_TOOL_PINCH);
+ RNA_enum_set(WM_keymap_add_item(keymap, "BRUSH_OT_uv_sculpt_tool_set", GKEY, KM_PRESS, 0, 0)->ptr, "tool", UV_SCULPT_TOOL_GRAB);
+
}
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");
+}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index e7a139fe465..2a8a5b3452f 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -178,6 +178,30 @@ int space_image_main_area_poll(bContext *C)
return 0;
}
+/* For IMAGE_OT_curves_point_set to avoid sampling when in uv smooth mode */
+int space_image_main_area_not_uv_brush_poll(bContext *C)
+{
+ SpaceImage *sima= CTX_wm_space_image(C);
+
+ ToolSettings *toolsettings = CTX_data_scene(C)->toolsettings;
+ if(sima && !toolsettings->uvsculpt)
+ return 1;
+
+ return 0;
+}
+
+static int space_image_image_sample_poll(bContext *C)
+{
+ SpaceImage *sima= CTX_wm_space_image(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ToolSettings *toolsettings = CTX_data_scene(C)->toolsettings;
+
+ if(obedit){
+ if(ED_space_image_show_uvedit(sima, obedit) && (toolsettings->use_uv_sculpt))
+ return 0;
+ }
+ return space_image_main_area_poll(C);
+}
/********************** view pan operator *********************/
typedef struct ViewPanData {
@@ -1949,7 +1973,7 @@ void IMAGE_OT_sample(wmOperatorType *ot)
ot->invoke= image_sample_invoke;
ot->modal= image_sample_modal;
ot->cancel= image_sample_cancel;
- ot->poll= space_image_main_area_poll;
+ ot->poll= space_image_image_sample_poll;
/* flags */
ot->flag= OPTYPE_BLOCKING;
@@ -2086,7 +2110,7 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot)
ot->invoke= image_sample_invoke;
ot->modal= image_sample_modal;
ot->cancel= image_sample_cancel;
- ot->poll= space_image_main_area_poll;
+ ot->poll= space_image_main_area_not_uv_brush_poll;
/* properties */
RNA_def_enum(ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves");
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index 6fce836af98..3d97972c829 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -770,6 +770,9 @@ static void image_main_area_init(wmWindowManager *wm, ARegion *ar)
keymap= WM_keymap_find(wm->defaultconf, "UV Editor", 0, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
+ keymap= WM_keymap_find(wm->defaultconf, "UV Sculpt", 0, 0);
+ WM_event_add_keymap_handler(&ar->handlers, keymap);
+
/* own keymaps */
keymap= WM_keymap_find(wm->defaultconf, "Image Generic", SPACE_IMAGE, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
@@ -782,6 +785,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar)
{
/* draw entirely, view changes should be handled here */
SpaceImage *sima= CTX_wm_space_image(C);
+ Object *obact= CTX_data_active_object(C);
Object *obedit= CTX_data_edit_object(C);
Scene *scene= CTX_data_scene(C);
View2D *v2d= &ar->v2d;
@@ -807,7 +811,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar)
/* and uvs in 0.0-1.0 space */
UI_view2d_view_ortho(v2d);
- draw_uvedit_main(sima, ar, scene, obedit);
+ draw_uvedit_main(sima, ar, scene, obedit, obact);
ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index d59cea3e77e..1c69e569aa6 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -40,6 +40,7 @@ set(SRC
uvedit_draw.c
uvedit_ops.c
uvedit_parametrizer.c
+ uvedit_smart_stitch.c
uvedit_unwrap_ops.c
uvedit_intern.h
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index 8f4a8ef540f..77609b9618b 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -427,12 +427,9 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, BMEditMesh *em, MTe
}
}
-static void draw_uvs_other(Scene *scene, Object *obedit, MTexPoly *activetf)
+static void draw_uvs_other(Scene *scene, Object *obedit, Image *curimage)
{
Base *base;
- Image *curimage;
-
- curimage= (activetf)? activetf->tpage: NULL;
glColor3ub(96, 96, 96);
@@ -468,6 +465,36 @@ static void draw_uvs_other(Scene *scene, Object *obedit, MTexPoly *activetf)
}
}
+static void draw_uvs_texpaint(SpaceImage *sima, Scene *scene, Object *ob)
+{
+ Mesh *me= ob->data;
+ Image *curimage = ED_space_image(sima);
+
+ if(sima->flag & SI_DRAW_OTHER)
+ draw_uvs_other(scene, ob, curimage);
+
+ glColor3ub(112, 112, 112);
+
+ if(me->mtface) {
+ MPoly *mface= me->mpoly;
+ MTexPoly *tface= me->mtpoly;
+ MLoopUV *mloopuv;
+ int a, b;
+
+ for(a=me->totpoly; a>0; a--, tface++, mface++) {
+ if(tface->tpage == curimage) {
+ glBegin(GL_LINE_LOOP);
+
+ mloopuv = me->mloopuv + mface->loopstart;
+ for (b=0; b<mface->totloop; b++, mloopuv++) {
+ glVertex2fv(mloopuv->uv);
+ }
+ glEnd();
+ }
+ }
+ }
+}
+
/* draws uv's in the image space */
static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
{
@@ -485,6 +512,12 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
int drawfaces, interpedges;
Image *ima= sima->image;
+#if 0 /* BMESH_TODO */
+ StitchPreviewer *stitch_preview = uv_get_stitch_previewer();
+#else
+ StitchPreviewer *stitch_preview = NULL;
+#endif
+
em= me->edit_btmesh;
activetf= EDBM_get_active_mtexpoly(em, &efa_act, 0); /* will be set to NULL if hidden */
activef = BM_get_actFace(em->bm, 0);
@@ -497,8 +530,11 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
interpedges= (ts->uv_selectmode == UV_SELECT_VERTEX);
/* draw other uvs */
- if(sima->flag & SI_DRAW_OTHER)
- draw_uvs_other(scene, obedit, activetf);
+ if(sima->flag & SI_DRAW_OTHER) {
+ Image *curimage= (activetf)? activetf->tpage: NULL;
+
+ draw_uvs_other(scene, obedit, curimage);
+ }
/* 1. draw shadow mesh */
@@ -575,7 +611,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
}
}
-
+
/* 3. draw active face stippled */
if(activef) {
@@ -846,23 +882,80 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
bglEnd();
}
+ /* finally draw stitch preview */
+ if(stitch_preview) {
+ glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glEnable(GL_BLEND);
+
+ UI_ThemeColor4(TH_STITCH_PREVIEW_ACTIVE);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glVertexPointer(2, GL_FLOAT, 0, stitch_preview->static_tris);
+ glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_static_tris*3);
+
+ glVertexPointer(2, GL_FLOAT, 0, stitch_preview->static_quads);
+ glDrawArrays(GL_QUADS, 0, stitch_preview->num_static_quads*4);
+
+ glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_tris);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ UI_ThemeColor4(TH_STITCH_PREVIEW_FACE);
+ glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ UI_ThemeColor4(TH_STITCH_PREVIEW_EDGE);
+ glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
+ /*UI_ThemeColor4(TH_STITCH_PREVIEW_VERT);
+ glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3);*/
+
+ glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_quads);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ UI_ThemeColor4(TH_STITCH_PREVIEW_FACE);
+ glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ UI_ThemeColor4(TH_STITCH_PREVIEW_EDGE);
+ glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
+ /*UI_ThemeColor4(TH_STITCH_PREVIEW_VERT);
+ glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4);*/
+
+ glDisable(GL_BLEND);
+
+ /* draw vert preview */
+ glPointSize(pointsize*2.0);
+ UI_ThemeColor4(TH_STITCH_PREVIEW_STITCHABLE);
+ glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_stitchable);
+ glDrawArrays(GL_POINTS, 0, stitch_preview->num_stitchable);
+
+ UI_ThemeColor4(TH_STITCH_PREVIEW_UNSTITCHABLE);
+ glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_unstitchable);
+ glDrawArrays(GL_POINTS, 0, stitch_preview->num_unstitchable);
+
+ glPopClientAttrib();
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+
glPointSize(1.0);
}
-void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit)
+void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit, Object *obact)
{
- int show_uvedit, show_uvshadow;
+ ToolSettings *toolsettings = scene->toolsettings;
+ int show_uvedit, show_uvshadow, show_texpaint_uvshadow;
+ show_texpaint_uvshadow = (obact && obact->type == OB_MESH && obact->mode == OB_MODE_TEXTURE_PAINT);
show_uvedit= ED_space_image_show_uvedit(sima, obedit);
show_uvshadow= ED_space_image_show_uvshadow(sima, obedit);
- if(show_uvedit || show_uvshadow) {
+ if(show_uvedit || show_uvshadow || show_texpaint_uvshadow) {
if(show_uvshadow)
draw_uvs_shadow(obedit);
- else
+ else if(show_uvedit)
draw_uvs(sima, scene, obedit);
+ else
+ draw_uvs_texpaint(sima, scene, obact);
- if(show_uvedit)
+ if(show_uvedit && !(toolsettings->use_uv_sculpt))
drawcursor_sima(sima, ar);
}
}
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index e7156c65491..58bdd8c3265 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -32,11 +32,15 @@
#ifndef ED_UVEDIT_INTERN_H
#define ED_UVEDIT_INTERN_H
-struct SpaceImage;
+struct EditFace;
+struct EditMesh;
struct MTexPoly;
-struct Scene;
struct Image;
+struct MTFace;
struct Object;
+struct Scene;
+struct SpaceImage;
+struct UvElementMap;
struct wmOperatorType;
struct BMEditMesh;
struct BMFace;
@@ -52,6 +56,7 @@ struct BMVert;
int uvedit_face_visible_nolocal(struct Scene *scene, struct BMFace *efa);
/* geometric utilities */
+
void uv_center(float uv[][2], float cent[2], int quad);
float uv_area(float uv[][2], int quad);
void uv_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy);
@@ -60,7 +65,55 @@ float poly_uv_area(float uv[][2], int len);
void poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len);
void poly_uv_center(struct BMEditMesh *em, struct BMFace *f, float cent[2]);
+/* find nearest */
+
+typedef struct NearestHit {
+ struct BMFace *efa;
+ struct MTexPoly *tf;
+ struct BMLoop *l, *nextl;
+ struct MLoopUV *luv, *nextluv;
+ int lindex; //index of loop within face
+ int vert1, vert2; //index in mesh of edge vertices
+} NearestHit;
+
+void uv_find_nearest_vert(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, float co[2], float penalty[2], struct NearestHit *hit);
+void uv_find_nearest_edge(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, float co[2], struct NearestHit *hit);
+
+/* utility tool functions */
+
+struct UvElement *ED_get_uv_element(struct UvElementMap *map, struct BMFace *efa, int index);
+void uvedit_live_unwrap_update(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit);
+
+/* smart stitch */
+
+/* object that stores display data for previewing before accepting stitching */
+typedef struct StitchPreviewer {
+ /* OpenGL requires different calls for Triangles and Quads.
+ * here we'll store the quads of the mesh */
+ float *preview_quads;
+ /* ...and here we'll store the triangles*/
+ float *preview_tris;
+ /* preview data. These will be either the previewed vertices or edges depending on stitch mode settings */
+ float *preview_stitchable;
+ float *preview_unstitchable;
+ /* here we'll store the number of triangles and quads to be drawn */
+ unsigned int num_tris;
+ unsigned int num_quads;
+ unsigned int num_stitchable;
+ unsigned int num_unstitchable;
+
+ /* store static island Quads */
+ float *static_quads;
+ /* ...and here we'll store the triangles*/
+ float *static_tris;
+ unsigned int num_static_tris;
+ unsigned int num_static_quads;
+} StitchPreviewer;
+
+StitchPreviewer *uv_get_stitch_previewer(void);
+
/* operators */
+
void UV_OT_average_islands_scale(struct wmOperatorType *ot);
void UV_OT_cube_project(struct wmOperatorType *ot);
void UV_OT_cylinder_project(struct wmOperatorType *ot);
@@ -70,6 +123,7 @@ void UV_OT_pack_islands(struct wmOperatorType *ot);
void UV_OT_reset(struct wmOperatorType *ot);
void UV_OT_sphere_project(struct wmOperatorType *ot);
void UV_OT_unwrap(struct wmOperatorType *ot);
+void UV_OT_stitch(struct wmOperatorType *ot);
#endif /* ED_UVEDIT_INTERN_H */
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index a11d5935e22..c468da6b937 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -20,7 +20,7 @@
*
* The Original Code is: all of this file.
*
- * Contributor(s): none yet.
+ * Contributor(s): Antony Riakiotakis.
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -103,6 +103,28 @@ int ED_uvedit_test(Object *obedit)
return ret;
}
+static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C)
+{
+ SpaceImage *sima= CTX_wm_space_image(C);
+ ToolSettings *toolsettings = CTX_data_tool_settings(C);
+ Object *obedit= CTX_data_edit_object(C);
+
+ return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt);
+}
+
+static int ED_operator_uvmap_mesh(bContext *C)
+{
+ Object *ob= CTX_data_active_object(C);
+
+ if(ob && ob->type==OB_MESH) {
+ Mesh *me = ob->data;
+
+ if(CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL)
+ return 1;
+ }
+
+ return 0;
+}
/**************************** object active image *****************************/
static int is_image_texture_node(bNode *node)
@@ -247,7 +269,13 @@ static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist
{
int width, height;
- ED_space_image_size(sima, &width, &height);
+ if(sima) {
+ ED_space_image_size(sima, &width, &height);
+ }
+ else {
+ width= 256;
+ height= 256;
+ }
dist[0]= pixeldist/width;
dist[1]= pixeldist/height;
@@ -468,7 +496,7 @@ void uvedit_uv_deselect(BMEditMesh *em, Scene *scene, BMLoop *l)
/*********************** live unwrap utilities ***********************/
-static void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
+void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
{
if(sima && (sima->flag & SI_LIVE_UNWRAP)) {
ED_uvedit_live_unwrap_begin(scene, obedit);
@@ -643,16 +671,7 @@ static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent,
/************************** find nearest ****************************/
-typedef struct NearestHit {
- BMFace *efa;
- MTexPoly *tf;
- BMLoop *l, *nextl;
- MLoopUV *luv, *nextluv;
- int lindex; //index of loop within face
- int vert1, vert2; //index in mesh of edge vertices
-} NearestHit;
-
-static void find_nearest_uv_edge(Scene *scene, Image *ima, BMEditMesh *em, float co[2], NearestHit *hit)
+void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, float co[2], NearestHit *hit)
{
MTexPoly *tf;
BMFace *efa;
@@ -712,7 +731,7 @@ static void find_nearest_uv_face(Scene *scene, Image *ima, BMEditMesh *em, float
memset(hit, 0, sizeof(*hit));
/*this will fill in hit.vert1 and hit.vert2*/
- find_nearest_uv_edge(scene, ima, em, co, hit);
+ uv_find_nearest_edge(scene, ima, em, co, hit);
hit->l = hit->nextl = NULL;
hit->luv = hit->nextluv = NULL;
@@ -786,8 +805,8 @@ static int nearest_uv_between(BMEditMesh *em, BMFace *efa, int UNUSED(nverts), i
return (c1*c2 >= 0.0f);
}
-static void find_nearest_uv_vert(Scene *scene, Image *ima, BMEditMesh *em,
- float co[2], float penalty[2], NearestHit *hit)
+void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em,
+ float co[2], float penalty[2], NearestHit *hit)
{
BMFace *efa;
BMLoop *l;
@@ -798,7 +817,7 @@ static void find_nearest_uv_vert(Scene *scene, Image *ima, BMEditMesh *em,
int i;
/*this will fill in hit.vert1 and hit.vert2*/
- find_nearest_uv_edge(scene, ima, em, co, hit);
+ uv_find_nearest_edge(scene, ima, em, co, hit);
hit->l = hit->nextl = NULL;
hit->luv = hit->nextluv = NULL;
@@ -918,6 +937,27 @@ static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, BMFace *efa, int a)
return NULL;
}
+/* BMESH_TODO - in some cases we already know the loop so looking up the index isnt needed */
+
+UvElement *ED_get_uv_element(UvElementMap *map, BMFace *efa, int index)
+{
+ BMLoop *loop = efa->loops.first;
+ UvElement *element;
+
+ while (index >= 0) {
+ loop = loop->next;
+ index--;
+ }
+
+ element = map->vert[BM_GetIndex(loop->v)];
+
+ for(; element; element = element->next)
+ if(element->face == efa)
+ return element;
+
+ return NULL;
+}
+
static int uv_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, UvMapVert *first2, int *totface)
{
UvMapVert *iterv1, *iterv2;
@@ -1536,6 +1576,7 @@ static void UV_OT_weld(wmOperatorType *ot)
ot->poll= ED_operator_uvedit;
}
+#if 0 // BMESH_TODO --- this function has been moved elsewhere
/* ******************** stitch operator **************** */
/* just for averaging UVs */
@@ -1703,6 +1744,8 @@ static void UV_OT_stitch(wmOperatorType *ot)
RNA_def_float(ot->srna, "limit", 20.0, 0.0f, FLT_MAX, "Limit", "Limit distance in image pixels", -FLT_MAX, FLT_MAX);
}
+#endif
+
/* ******************** (de)select all operator **************** */
static void select_all_perform(bContext *C, int action)
@@ -1891,7 +1934,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop)
/* find nearest element */
if(loop) {
/* find edge */
- find_nearest_uv_edge(scene, ima, em, co, &hit);
+ uv_find_nearest_edge(scene, ima, em, co, &hit);
if(hit.efa == NULL) {
BLI_array_free(hitv);
BLI_array_free(hituv);
@@ -1902,7 +1945,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop)
}
else if(selectmode == UV_SELECT_VERTEX) {
/* find vertex */
- find_nearest_uv_vert(scene, ima, em, co, penalty, &hit);
+ uv_find_nearest_vert(scene, ima, em, co, penalty, &hit);
if(hit.efa == NULL) {
BLI_array_free(hitv);
BLI_array_free(hituv);
@@ -1923,7 +1966,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop)
}
else if(selectmode == UV_SELECT_EDGE) {
/* find edge */
- find_nearest_uv_edge(scene, ima, em, co, &hit);
+ uv_find_nearest_edge(scene, ima, em, co, &hit);
if(hit.efa == NULL) {
BLI_array_free(hitv);
BLI_array_free(hituv);
@@ -1973,7 +2016,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop)
hitlen = hit.efa->len;
}
else if(selectmode == UV_SELECT_ISLAND) {
- find_nearest_uv_vert(scene, ima, em, co, NULL, &hit);
+ uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
if(hit.efa==NULL) {
BLI_array_free(hitv);
@@ -2257,7 +2300,7 @@ static int select_linked_internal(bContext *C, wmOperator *op, wmEvent *event, i
RNA_float_get_array(op->ptr, "location", co);
}
- find_nearest_uv_vert(scene, ima, em, co, NULL, &hit);
+ uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
hit_p= &hit;
}
@@ -3457,6 +3500,183 @@ static void UV_OT_tile_set(wmOperatorType *ot)
RNA_def_int_vector(ot->srna, "tile", 2, NULL, 0, INT_MAX, "Tile", "Tile coordinate", 0, 10);
}
+
+static int seams_from_islands_exec(bContext *C, wmOperator *op)
+{
+ UvVertMap *vmap;
+ Object *ob = CTX_data_edit_object(C);
+ Mesh *me= (Mesh*)ob->data;
+ BMEditMesh *em;
+ BMEdge *editedge;
+ float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT};
+ char mark_seams = RNA_boolean_get(op->ptr, "mark_seams");
+ char mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp");
+
+ BMesh *bm;
+ BMIter iter;
+
+ em = me->edit_btmesh;
+ bm = em->bm;
+
+ if(!EDBM_texFaceCheck(em)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* This code sets editvert->tmp.l to the index. This will be useful later on. */
+ EDBM_init_index_arrays(em, 0, 0, 1);
+ vmap = EDBM_make_uv_vert_map(em, 0, 0, limit);
+
+ BM_ITER(editedge, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ /* flags to determine if we uv is separated from first editface match */
+ char separated1 = 0, separated2;
+ /* set to denote edge must be flagged as seam */
+ char faces_separated = 0;
+ /* flag to keep track if uv1 is disconnected from first editface match */
+ char v1coincident = 1;
+ /* For use with v1coincident. v1coincident will change only if we've had commonFaces */
+ int commonFaces = 0;
+
+ BMFace *efa1, *efa2;
+
+ UvMapVert *mv1, *mvinit1, *mv2, *mvinit2, *mviter;
+ /* mv2cache stores the first of the list of coincident uv's for later comparison
+ * mv2sep holds the last separator and is copied to mv2cache when a hit is first found */
+ UvMapVert *mv2cache = NULL, *mv2sep = NULL;
+
+ mvinit1 = vmap->vert[BM_GetIndex(editedge->v1)];
+ if(mark_seams)
+ BM_ClearHFlag(editedge, BM_SEAM);
+
+ for(mv1 = mvinit1; mv1 && !faces_separated; mv1 = mv1->next) {
+ if(mv1->separate && commonFaces)
+ v1coincident = 0;
+
+ separated2 = 0;
+ efa1 = EDBM_get_face_for_index(em, mv1->f);
+ mvinit2 = vmap->vert[BM_GetIndex(editedge->v2)];
+
+ for(mv2 = mvinit2; mv2; mv2 = mv2->next) {
+ if(mv2->separate)
+ mv2sep = mv2;
+
+ efa2 = EDBM_get_face_for_index(em, mv2->f);
+ if(efa1 == efa2) {
+ /* if v1 is not coincident no point in comparing */
+ if(v1coincident) {
+ /* have we found previously anything? */
+ if(mv2cache) {
+ /* flag seam unless proved to be coincident with previous hit */
+ separated2 = 1;
+ for(mviter = mv2cache; mviter; mviter = mviter->next) {
+ if(mviter->separate && mviter != mv2cache)
+ break;
+ /* coincident with previous hit, do not flag seam */
+ if(mviter == mv2)
+ separated2 = 0;
+ }
+ }
+ /* First hit case, store the hit in the cache */
+ else {
+ mv2cache = mv2sep;
+ commonFaces = 1;
+ }
+ }
+ else
+ separated1 = 1;
+
+ if(separated1 || separated2) {
+ faces_separated = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if(faces_separated) {
+ if(mark_seams)
+ BM_SetHFlag(editedge, BM_SEAM);
+ if(mark_sharp)
+ BM_SetHFlag(editedge, BM_SHARP);
+ }
+ }
+
+ me->drawflag |= ME_DRAWSEAMS;
+
+ EDBM_free_uv_vert_map(vmap);
+ EDBM_free_index_arrays(em);
+
+ DAG_id_tag_update(&me->id, 0);
+ WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
+
+ return OPERATOR_FINISHED;
+}
+
+
+static void UV_OT_seams_from_islands(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Seams From Islands";
+ ot->description= "Set mesh seams according to island setup in the UV editor";
+ ot->idname= "UV_OT_seams_from_islands";
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec= seams_from_islands_exec;
+ ot->poll= ED_operator_uvedit;
+
+ RNA_def_boolean(ot->srna, "mark_seams", 1, "Mark Seams", "Mark boundary edges as seams");
+ RNA_def_boolean(ot->srna, "mark_sharp", 0, "Mark Sharp", "Mark boundary edges as sharp");
+}
+
+static int mark_seam_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_edit_object(C);
+ Scene *scene = CTX_data_scene(C);
+ Mesh *me= (Mesh*)ob->data;
+ BMEditMesh *em= me->edit_btmesh;
+ BMesh *bm = em->bm;
+ BMFace *efa;
+ BMLoop *loop;
+
+ BMIter iter, liter;
+
+ BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
+ BM_ITER(loop, &liter, bm, BM_LOOPS_OF_FACE, efa) {
+ if(uvedit_edge_selected(em, scene, loop)) {
+ BM_SetHFlag(loop, BM_SEAM);
+ }
+ }
+ }
+
+ me->drawflag |= ME_DRAWSEAMS;
+
+ if(scene->toolsettings->edge_mode_live_unwrap)
+ ED_unwrap_lscm(scene, ob, FALSE);
+
+ DAG_id_tag_update(&me->id, 0);
+ WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
+
+ return OPERATOR_FINISHED;
+}
+
+static void UV_OT_mark_seam(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Mark Seams";
+ ot->description= "Mark selected UV edges as seams";
+ ot->idname= "UV_OT_mark_seam";
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec= mark_seam_exec;
+ ot->poll= ED_operator_uvedit;
+}
+
+
/* ************************** registration **********************************/
void ED_operatortypes_uvedit(void)
@@ -3475,7 +3695,11 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_snap_selected);
WM_operatortype_append(UV_OT_align);
+#if 0 /* BMESH_TODO */
WM_operatortype_append(UV_OT_stitch);
+#endif
+ WM_operatortype_append(UV_OT_seams_from_islands);
+ WM_operatortype_append(UV_OT_mark_seam);
WM_operatortype_append(UV_OT_weld);
WM_operatortype_append(UV_OT_pin);
@@ -3502,7 +3726,14 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf)
wmKeyMapItem *kmi;
keymap= WM_keymap_find(keyconf, "UV Editor", 0, 0);
- keymap->poll= ED_operator_uvedit;
+ keymap->poll= ED_operator_uvedit_can_uv_sculpt;
+
+ /* Uv sculpt toggle */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, 0, 0);
+ RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt");
+
+ /* Mark edge seam */
+ WM_keymap_add_item(keymap, "UV_OT_mark_seam", EKEY, KM_PRESS, KM_CTRL, 0);
/* pick selection */
RNA_boolean_set(WM_keymap_add_item(keymap, "UV_OT_select", SELECTMOUSE, KM_PRESS, 0, 0)->ptr, "extend", FALSE);
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index 9bb4d655a59..68c8669ccc7 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -91,7 +91,7 @@ typedef struct PVert {
} u;
struct PEdge *edge;
- float *co;
+ float co[3];
float uv[2];
unsigned char flag;
@@ -655,11 +655,15 @@ static void p_face_backup_uvs(PFace *f)
{
PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
- if (e1->orig_uv && e2->orig_uv && e3->orig_uv) {
+ if (e1->orig_uv) {
e1->old_uv[0] = e1->orig_uv[0];
e1->old_uv[1] = e1->orig_uv[1];
+ }
+ if (e2->orig_uv) {
e2->old_uv[0] = e2->orig_uv[0];
e2->old_uv[1] = e2->orig_uv[1];
+ }
+ if (e3->orig_uv) {
e3->old_uv[0] = e3->orig_uv[0];
e3->old_uv[1] = e3->orig_uv[1];
}
@@ -669,11 +673,15 @@ static void p_face_restore_uvs(PFace *f)
{
PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
- if (e1->orig_uv && e2->orig_uv && e3->orig_uv) {
+ if (e1->orig_uv) {
e1->orig_uv[0] = e1->old_uv[0];
e1->orig_uv[1] = e1->old_uv[1];
+ }
+ if (e2->orig_uv) {
e2->orig_uv[0] = e2->old_uv[0];
e2->orig_uv[1] = e2->old_uv[1];
+ }
+ if (e3->orig_uv) {
e3->orig_uv[0] = e3->old_uv[0];
e3->orig_uv[1] = e3->old_uv[1];
}
@@ -684,7 +692,7 @@ static void p_face_restore_uvs(PFace *f)
static PVert *p_vert_add(PHandle *handle, PHashKey key, float *co, PEdge *e)
{
PVert *v = (PVert*)BLI_memarena_alloc(handle->arena, sizeof *v);
- v->co = co;
+ copy_v3_v3(v->co, co);
v->u.key = key;
v->edge = e;
v->flag = 0;
@@ -708,7 +716,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v)
{
PVert *nv = (PVert*)BLI_memarena_alloc(chart->handle->arena, sizeof *nv);
- nv->co = v->co;
+ copy_v3_v3(nv->co, v->co);
nv->uv[0] = v->uv[0];
nv->uv[1] = v->uv[1];
nv->u.key = v->u.key;
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
new file mode 100644
index 00000000000..cc139544ce3
--- /dev/null
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -0,0 +1,1446 @@
+
+#if 0 /* BMESH TODO */
+
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Antony Riakiotakis.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/uvedit/uvedit_stitch.c
+ * \ingroup eduv
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_editVert.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mesh.h"
+#include "BKE_tessmesh.h"
+
+#include "ED_mesh.h"
+#include "ED_uvedit.h"
+#include "ED_screen.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_view2d.h"
+
+#include "uvedit_intern.h"
+
+/* ********************** smart stitch operator *********************** */
+
+
+struct IslandStitchData;
+
+/* This is a straightforward implementation, count the uv's in the island that will move and take the mean displacement/rotation and apply it to all
+ * elements of the island except from the stitchable */
+typedef struct IslandStitchData{
+ /* rotation can be used only for edges, for vertices there is no such notion */
+ float rotation;
+ float translation[2];
+ /* Used for rotation, the island will rotate around this point */
+ float medianPoint[2];
+ int numOfElements;
+ int num_rot_elements;
+ /* Flag to remember if island has been added for preview */
+ char addedForPreview;
+ /* Flag an island to be considered for determining static island */
+ char stitchableCandidate;
+}IslandStitchData;
+
+/* just for averaging UVs */
+typedef struct UVVertAverage {
+ float uv[2];
+ unsigned short count;
+} UVVertAverage;
+
+typedef struct UvEdge {
+ /* index to uv buffer */
+ unsigned int uv1;
+ unsigned int uv2;
+ /* general use flag (Used to check if edge is boundary here, and propagates to adjacency elements) */
+ char flag;
+ /* element that guarantees element->face has the face on element->tfindex and element->tfindex+1 is the second uv */
+ UvElement *element;
+}UvEdge;
+
+
+/* stitch state object */
+typedef struct StitchState {
+ /* use limit flag */
+ char use_limit;
+ /* limit to operator, same as original operator */
+ float limit_dist;
+ /* snap uv islands together during stitching */
+ char snap_islands;
+ /* stich at midpoints or at islands */
+ char midpoints;
+ /* editmesh, cached for use in modal handler */
+ EditMesh *em;
+ /* element map for getting info about uv connectivity */
+ UvElementMap *element_map;
+ /* edge container */
+ UvEdge *uvedges;
+ /* container of first of a group of coincident uvs, these will be operated upon */
+ UvElement **uvs;
+ /* maps uvelements to their first coincident uv */
+ int *map;
+ /* 2D normals per uv to calculate rotation for snapping */
+ float *normals;
+ /* edge storage */
+ UvEdge *edges;
+
+ /* count of separate uvs and edges */
+ int total_boundary_edges;
+ int total_separate_uvs;
+ /* hold selection related information */
+ UvElement **selection_stack;
+ int selection_size;
+ /* island that stays in place */
+ int static_island;
+ /* store number of primitives per face so that we can allocate the active island buffer later */
+ unsigned int *quads_per_island;
+ unsigned int *tris_per_island;
+} StitchState;
+
+
+/*
+ * defines for UvElement flags
+ */
+#define STITCH_SELECTED 1
+#define STITCH_STITCHABLE 2
+#define STITCH_PROCESSED 4
+#define STITCH_BOUNDARY 8
+#define STITCH_STITCHABLE_CANDIDATE 16
+
+#define STITCH_NO_PREVIEW -1
+
+/* previewer stuff (see uvedit_intern.h for more info) */
+static StitchPreviewer *_stitch_preview;
+
+/* constructor */
+static StitchPreviewer * stitch_preview_init(void)
+{
+ _stitch_preview = MEM_mallocN(sizeof(StitchPreviewer), "stitch_previewer");
+ _stitch_preview->preview_quads = NULL;
+ _stitch_preview->preview_tris = NULL;
+ _stitch_preview->preview_stitchable = NULL;
+ _stitch_preview->preview_unstitchable = NULL;
+
+ _stitch_preview->num_quads = 0;
+ _stitch_preview->num_tris = 0;
+ _stitch_preview->num_stitchable = 0;
+ _stitch_preview->num_unstitchable = 0;
+
+ _stitch_preview->static_quads = NULL;
+ _stitch_preview->static_tris = NULL;
+
+ _stitch_preview->num_static_tris = 0;
+ _stitch_preview->num_static_quads = 0;
+
+ return _stitch_preview;
+}
+
+/* destructor...yeah this should be C++ :) */
+static void stitch_preview_delete(void)
+{
+ if(_stitch_preview)
+ {
+ if(_stitch_preview->preview_quads){
+ MEM_freeN(_stitch_preview->preview_quads);
+ _stitch_preview->preview_quads = NULL;
+ }
+ if(_stitch_preview->preview_tris){
+ MEM_freeN(_stitch_preview->preview_tris);
+ _stitch_preview->preview_tris = NULL;
+ }
+ if(_stitch_preview->preview_stitchable){
+ MEM_freeN(_stitch_preview->preview_stitchable);
+ _stitch_preview->preview_stitchable = NULL;
+ }
+ if(_stitch_preview->preview_unstitchable){
+ MEM_freeN(_stitch_preview->preview_unstitchable);
+ _stitch_preview->preview_unstitchable = NULL;
+ }
+ if(_stitch_preview->static_quads){
+ MEM_freeN(_stitch_preview->static_quads);
+ _stitch_preview->static_quads = NULL;
+ }
+ if(_stitch_preview->static_tris){
+ MEM_freeN(_stitch_preview->static_tris);
+ _stitch_preview->static_tris = NULL;
+ }
+ MEM_freeN(_stitch_preview);
+ _stitch_preview = NULL;
+ }
+}
+
+
+/* "getter method" */
+StitchPreviewer *uv_get_stitch_previewer(void)
+{
+ return _stitch_preview;
+}
+
+#define HEADER_LENGTH 256
+
+/* This function updates the header of the UV editor when the stitch tool updates its settings */
+static void stitch_update_header(StitchState *stitch_state, bContext *C)
+{
+ static char str[] = "(S)nap %s, (M)idpoints %s, (L)imit %.2f (Alt Wheel adjust) %s, Switch (I)sland, shift select vertices";
+
+ char msg[HEADER_LENGTH];
+ ScrArea *sa= CTX_wm_area(C);
+
+ if(sa) {
+ BLI_snprintf(msg, HEADER_LENGTH, str,
+ stitch_state->snap_islands? "On" : "Off",
+ stitch_state->midpoints? "On": "Off",
+ stitch_state->limit_dist,
+ stitch_state->use_limit? "On" : "Off");
+
+ ED_area_headerprint(sa, msg);
+ }
+}
+
+static int getNumOfIslandUvs(UvElementMap *elementMap, int island){
+ if(island == elementMap->totalIslands-1){
+ return elementMap->totalUVs - elementMap->islandIndices[island];
+ }else{
+ return elementMap->islandIndices[island+1] - elementMap->islandIndices[island];
+ }
+}
+
+static void stitch_uv_rotate(float rotation, float medianPoint[2], float uv[2]){
+ float uv_rotation_result[2];
+
+ uv[0] -= medianPoint[0];
+ uv[1] -= medianPoint[1];
+
+ uv_rotation_result[0] = cos(rotation)*uv[0] - sin(rotation)*uv[1];
+ uv_rotation_result[1] = sin(rotation)*uv[0] + cos(rotation)*uv[1];
+
+ uv[0] = uv_rotation_result[0] + medianPoint[0];
+ uv[1] = uv_rotation_result[1] + medianPoint[1];
+}
+
+
+/* calculate snapping for islands */
+static void stitch_calculate_island_snapping(StitchState *state, StitchPreviewer *preview, IslandStitchData *island_stitch_data, int final){
+ int i;
+ EditFace *efa;
+ MTFace *mt;
+ UvElement *element;
+
+ for(i = 0; i < state->element_map->totalIslands; i++){
+ if(island_stitch_data[i].addedForPreview){
+ int numOfIslandUVs = 0, j;
+
+ /* check to avoid divide by 0 */
+ if(island_stitch_data[i].num_rot_elements>0){
+ island_stitch_data[i].rotation /= island_stitch_data[i].num_rot_elements;
+ island_stitch_data[i].medianPoint[0] /= island_stitch_data[i].numOfElements;
+ island_stitch_data[i].medianPoint[1] /= island_stitch_data[i].numOfElements;
+ }
+ island_stitch_data[i].translation[0] /= island_stitch_data[i].numOfElements;
+ island_stitch_data[i].translation[1] /= island_stitch_data[i].numOfElements;
+ numOfIslandUVs = getNumOfIslandUvs(state->element_map, i);
+ element = &state->element_map->buf[state->element_map->islandIndices[i]];
+ for(j = 0; j < numOfIslandUVs; j++, element++){
+ /* stitchable uvs have already been processed, don't process */
+ if(!(element->flag & STITCH_PROCESSED)){
+ efa = element->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+ if(final){
+
+ stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, mt->uv[element->tfindex]);
+
+ mt->uv[element->tfindex][0] += island_stitch_data[i].translation[0];
+ mt->uv[element->tfindex][1] += island_stitch_data[i].translation[1];
+ }
+ else if(efa->tmp.l != STITCH_NO_PREVIEW){
+ if(efa->v4){
+
+ stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, &preview->preview_quads[efa->tmp.l + 2*element->tfindex]);
+
+ preview->preview_quads[efa->tmp.l + 2*element->tfindex] += island_stitch_data[i].translation[0];
+ preview->preview_quads[efa->tmp.l + 2*element->tfindex + 1] += island_stitch_data[i].translation[1];
+ }
+ else {
+
+ stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, &preview->preview_tris[efa->tmp.l + 2*element->tfindex]);
+
+ preview->preview_tris[efa->tmp.l + 2*element->tfindex] += island_stitch_data[i].translation[0];
+ preview->preview_tris[efa->tmp.l + 2*element->tfindex + 1] += island_stitch_data[i].translation[1];
+ }
+ }
+ }
+ /* cleanup */
+ element->flag &= STITCH_SELECTED;
+ }
+ }
+ }
+}
+
+
+
+static void stitch_island_calculate_edge_rotation(UvEdge *edge, StitchState *state, UVVertAverage *uv_average, unsigned int *uvfinal_map, IslandStitchData *island_stitch_data)
+{
+ UvElement *element;
+ EditFace *efa;
+ MTFace *mt;
+ int nverts;
+ float uv1[2], uv2[2];
+ float edgecos, edgesin;
+ int index1, index2;
+
+ element = edge->element;
+ efa = element->face;
+ nverts = (efa->v4)? 4 : 3;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+
+ index1 = uvfinal_map[(*(&element->face->v1 + element->tfindex))->tmp.l];
+ index2 = uvfinal_map[(*(&element->face->v1 + (element->tfindex + 1)%nverts))->tmp.l];
+
+ /* the idea here is to take the directions of the edges and find the rotation between final and initial
+ * direction. This, using inner and outer vector products, gives the angle. Directions are differences so... */
+ uv1[0] = mt->uv[(element->tfindex + 1)%nverts][0] - mt->uv[element->tfindex][0];
+ uv1[1] = mt->uv[(element->tfindex + 1)%nverts][1] - mt->uv[element->tfindex][1];
+
+ uv2[0] = uv_average[index2].uv[0] - uv_average[index1].uv[0];
+ uv2[1] = uv_average[index2].uv[1] - uv_average[index1].uv[1];
+
+ normalize_v2(uv1);
+ normalize_v2(uv2);
+
+ edgecos = uv1[0]*uv2[0] + uv1[1]*uv2[1];
+ edgesin = uv1[0]*uv2[1] - uv2[0]*uv1[1];
+ island_stitch_data[element->island].num_rot_elements++;
+ island_stitch_data[element->island].rotation += (edgesin > 0)? acos(MAX2(-1.0, MIN2(1.0, edgecos))): -acos(MAX2(-1.0, MIN2(1.0, edgecos)));
+}
+
+static void stitch_island_calculate_vert_rotation(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data, char do_static)
+{
+ float edgecos = 1, edgesin = 0;
+ int index;
+ UvElement *element_iter;
+
+ if((element->island == state->static_island) && !do_static)
+ return;
+
+ index = (*(&element->face->v1 + element->tfindex))->tmp.l;
+
+ element_iter = state->element_map->vert[index];
+
+ if(!do_static){
+ for(; element_iter; element_iter = element_iter->next){
+ if((element_iter->separate) && (element_iter->flag & STITCH_STITCHABLE) &&
+ (element_iter != element) && (element_iter->island == state->static_island)
+ ){
+ int index_tmp1, index_tmp2;
+ float normal[2];
+ /* easily possible*/
+
+ index_tmp1 = element_iter - state->element_map->buf;
+ index_tmp1 = state->map[index_tmp1];
+ index_tmp2 = element - state->element_map->buf;
+ index_tmp2 = state->map[index_tmp2];
+
+ negate_v2_v2(normal, state->normals + index_tmp2*2);
+ edgecos = dot_v2v2(normal, state->normals + index_tmp1*2);
+ edgesin = cross_v2v2(normal, state->normals + index_tmp1*2);
+ break;
+ }
+ }
+ }
+
+ island_stitch_data[element->island].num_rot_elements++;
+ island_stitch_data[element->island].rotation += (edgesin > 0)? acos(edgecos): -acos(edgecos);
+}
+
+
+static void stitch_state_delete(StitchState *stitch_state)
+{
+ if(stitch_state){
+ if(stitch_state->element_map){
+ EDBM_free_uv_element_map(stitch_state->element_map);
+ }
+ if(stitch_state->uvs){
+ MEM_freeN(stitch_state->uvs);
+ }
+ if(stitch_state->selection_stack){
+ MEM_freeN(stitch_state->selection_stack);
+ }
+ if(stitch_state->quads_per_island){
+ MEM_freeN(stitch_state->quads_per_island);
+ }
+ if(stitch_state->tris_per_island){
+ MEM_freeN(stitch_state->tris_per_island);
+ }
+ if(stitch_state->map){
+ MEM_freeN(stitch_state->map);
+ }
+ if(stitch_state->normals){
+ MEM_freeN(stitch_state->normals);
+ }
+ if(stitch_state->edges){
+ MEM_freeN(stitch_state->edges);
+ }
+ MEM_freeN(stitch_state);
+ }
+}
+
+
+
+/* checks for remote uvs that may be stitched with a certain uv, flags them if stitchable. */
+static void determine_uv_stitchability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){
+ int vert_index;
+ UvElement *element_iter;
+ float limit= state->limit_dist;
+ int do_limit = state->use_limit;
+
+ vert_index = (*(&element->face->v1 + element->tfindex))->tmp.l;
+ element_iter = state->element_map->vert[vert_index];
+
+ for(; element_iter; element_iter = element_iter->next){
+ if(element_iter->separate){
+ if(element_iter == element){
+ continue;
+ }
+ if(do_limit){
+ MTFace *mtface_orig = CustomData_em_get(&state->em->fdata, element->face->data, CD_MTFACE);
+ MTFace *mtface_iter = CustomData_em_get(&state->em->fdata, element_iter->face->data, CD_MTFACE);
+
+ if(fabs(mtface_orig->uv[element->tfindex][0] - mtface_iter->uv[element_iter->tfindex][0]) < limit
+ && fabs(mtface_orig->uv[element->tfindex][1] - mtface_iter->uv[element_iter->tfindex][1]) < limit){
+ island_stitch_data[element_iter->island].stitchableCandidate = 1;
+ island_stitch_data[element->island].stitchableCandidate = 1;
+ element->flag |= STITCH_STITCHABLE_CANDIDATE;
+ }
+ }else{
+ /* if no limit exists, then the mere existence of a separate uv means that the uv is stitchable */
+ island_stitch_data[element_iter->island].stitchableCandidate = 1;
+ island_stitch_data[element->island].stitchableCandidate = 1;
+ element->flag |= STITCH_STITCHABLE_CANDIDATE;
+ }
+ }
+ }
+}
+
+
+
+/* set preview buffer position of UV face in editface->tmp.l */
+static void stitch_set_face_preview_buffer_position(EditFace *efa, StitchPreviewer *preview)
+{
+ if(efa->tmp.l == STITCH_NO_PREVIEW)
+ {
+ if(efa->v4)
+ {
+ efa->tmp.l = preview->num_quads*8;
+ preview->num_quads++;
+ } else {
+ efa->tmp.l = preview->num_tris*6;
+ preview->num_tris++;
+ }
+ }
+}
+
+
+/* setup face preview for all coincident uvs and their faces */
+static void stitch_setup_face_preview_for_uv_group(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){
+ StitchPreviewer *preview = uv_get_stitch_previewer();
+
+ /* static island does not change so returning immediately */
+ //if(state->snap_islands && !state->midpoints && state->static_island == element->island)
+ // return;
+
+ if(state->snap_islands){
+ island_stitch_data[element->island].addedForPreview = 1;
+ }
+
+ do{
+ stitch_set_face_preview_buffer_position(element->face, preview);
+ element = element->next;
+ }while(element && !element->separate);
+}
+
+
+/* checks if uvs are indeed stitchable and registers so that they can be shown in preview */
+static void stitch_validate_stichability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){
+ UvElement *element_iter;
+ StitchPreviewer *preview;
+
+ preview = uv_get_stitch_previewer();
+ element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l];
+
+ for(; element_iter; element_iter = element_iter->next){
+ if(element_iter->separate){
+ if(element_iter == element)
+ continue;
+ if(state->use_limit){
+ MTFace *mtface_orig = CustomData_em_get(&state->em->fdata, element->face->data, CD_MTFACE);
+ MTFace *mtface_iter = CustomData_em_get(&state->em->fdata, element_iter->face->data, CD_MTFACE);
+
+ if(fabs(mtface_orig->uv[element->tfindex][0] - mtface_iter->uv[element_iter->tfindex][0]) < state->limit_dist
+ && fabs(mtface_orig->uv[element->tfindex][1] - mtface_iter->uv[element_iter->tfindex][1]) < state->limit_dist){
+ if(((element_iter->island == state->static_island) || (element->island == state->static_island)) &&
+ !((element_iter->island == element->island) && state->snap_islands)){
+ element->flag |= STITCH_STITCHABLE;
+ preview->num_stitchable++;
+ stitch_setup_face_preview_for_uv_group(element, state, island_stitch_data);
+ return;
+ }
+ }
+ }else{
+ if(((element_iter->island == state->static_island) || (element->island == state->static_island)) &&
+ !((element_iter->island == element->island) && state->snap_islands)){
+ element->flag |= STITCH_STITCHABLE;
+ preview->num_stitchable++;
+ stitch_setup_face_preview_for_uv_group(element, state, island_stitch_data);
+ return;
+ }
+ }
+ }
+ }
+
+ /* this can happen if the uvs to be stitched are not on a stitchable island */
+ if(!(element->flag & STITCH_STITCHABLE)){
+ preview->num_unstitchable++;
+ }
+}
+
+/* main processing function. It calculates preview and final positions. */
+static int stitch_process_data(StitchState *state, Scene *scene, int final)
+{
+ int i;
+ StitchPreviewer *preview = uv_get_stitch_previewer();
+ IslandStitchData *island_stitch_data = NULL;
+ int previous_island = state->static_island;
+ EditFace *efa;
+ EditVert *ev;
+ UVVertAverage *final_position;
+ char stitch_midpoints = state->midpoints;
+ /* use vertex normals for snapping rotation */
+ char use_vert_normals = 1;
+ /* used to map uv indices to uvaverage indices for selection */
+ unsigned int *uvfinal_map;
+
+ /* cleanup previous preview */
+ stitch_preview_delete();
+ preview = stitch_preview_init();
+ if(preview == NULL)
+ return 0;
+ /* each face holds its position in the preview buffer in tmp. -1 is uninitialized */
+ for(efa = state->em->faces.first; efa; efa = efa->next){
+ efa->tmp.l = STITCH_NO_PREVIEW;
+ }
+
+ island_stitch_data = MEM_callocN(sizeof(*island_stitch_data)*state->element_map->totalIslands, "stitch_island_data");
+ if(!island_stitch_data){
+ return 0;
+ }
+
+ /* store Indices to editVerts. */
+ for(ev = state->em->verts.first, i = 0; ev; ev = ev->next, i++){
+ ev->tmp.l = i;
+ }
+
+ /*****************************************
+ * First determine stitchability of uvs *
+ *****************************************/
+
+ for(i = 0; i < state->selection_size; i++){
+ UvElement *element = state->selection_stack[i];
+ determine_uv_stitchability(element, state, island_stitch_data);
+ }
+
+ /* set static island to one that is added for preview */
+ state->static_island %= state->element_map->totalIslands;
+ while(!(island_stitch_data[state->static_island].stitchableCandidate)){
+ state->static_island++;
+ state->static_island %= state->element_map->totalIslands;
+ /* this is entirely possible if for example limit stitching with no stitchable verts or no selection */
+ if(state->static_island == previous_island)
+ break;
+ }
+
+ for(i = 0; i < state->selection_size; i++){
+ UvElement *element = state->selection_stack[i];
+ if(element->flag & STITCH_STITCHABLE_CANDIDATE){
+ element->flag &= ~STITCH_STITCHABLE_CANDIDATE;
+ stitch_validate_stichability(element, state, island_stitch_data);
+ }else{
+ /* add to preview for unstitchable */
+ preview->num_unstitchable++;
+ }
+ }
+
+ /*****************************************
+ * Setup preview for stitchable islands *
+ *****************************************/
+ if(state->snap_islands){
+ for(i = 0; i < state->element_map->totalIslands; i++){
+ if(island_stitch_data[i].addedForPreview){
+ int numOfIslandUVs = 0, j;
+ UvElement *element;
+ numOfIslandUVs = getNumOfIslandUvs(state->element_map, i);
+ element = &state->element_map->buf[state->element_map->islandIndices[i]];
+ for(j = 0; j < numOfIslandUVs; j++, element++){
+ stitch_set_face_preview_buffer_position(element->face, preview);
+ }
+ }
+ }
+ }
+
+ /*********************************************************************
+ * Setup the preview buffers and fill them with the appropriate data *
+ *********************************************************************/
+ if(!final){
+ unsigned int tricount = 0, quadcount = 0;
+ int stitchBufferIndex = 0, unstitchBufferIndex = 0;
+ /* initialize the preview buffers */
+ preview->preview_quads = (float *)MEM_mallocN(preview->num_quads*sizeof(float)*8, "quad_uv_stitch_prev");
+ preview->preview_tris = (float *)MEM_mallocN(preview->num_tris*sizeof(float)*6, "tri_uv_stitch_prev");
+
+ preview->preview_stitchable = (float *)MEM_mallocN(preview->num_stitchable*sizeof(float)*2, "stitch_preview_stichable_data");
+ preview->preview_unstitchable = (float *)MEM_mallocN(preview->num_unstitchable*sizeof(float)*2, "stitch_preview_unstichable_data");
+
+ preview->static_quads = (float *)MEM_mallocN(state->quads_per_island[state->static_island]*sizeof(float)*8, "static_island_preview_quads");
+ preview->static_tris = (float *)MEM_mallocN(state->tris_per_island[state->static_island]*sizeof(float)*6, "static_island_preview_tris");
+
+ preview->num_static_quads = state->quads_per_island[state->static_island];
+ preview->num_static_tris = state->tris_per_island[state->static_island];
+ /* will cause cancel and freeing of all data structures so OK */
+ if(!preview->preview_quads || !preview->preview_tris || !preview->preview_stitchable || !preview->preview_unstitchable){
+ return 0;
+ }
+
+ /* copy data from MTFaces to the preview display buffers */
+ for(efa = state->em->faces.first; efa; efa = efa->next){
+ MTFace *mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+ UvElement *element = ED_get_uv_element(state->element_map, efa, 0);
+
+ if(element){
+ if(efa->tmp.l != STITCH_NO_PREVIEW){
+ if(efa->v4) {
+ memcpy(preview->preview_quads+efa->tmp.l, &mt->uv[0][0], 8*sizeof(float));
+ } else {
+ memcpy(preview->preview_tris+efa->tmp.l, &mt->uv[0][0], 6*sizeof(float));
+ }
+ }
+
+ if(element->island == state->static_island){
+ if(efa->v4) {
+ memcpy(preview->static_quads + quadcount*8, &mt->uv[0][0], 8*sizeof(float));
+ quadcount++;
+ } else {
+ memcpy(preview->static_tris + tricount*6, &mt->uv[0][0], 6*sizeof(float));
+ tricount++;
+ }
+ }
+ }
+ }
+
+ /* fill the appropriate preview buffers */
+ for(i = 0; i < state->total_separate_uvs; i++){
+ UvElement *element = (UvElement *)state->uvs[i];
+ if(element->flag & STITCH_STITCHABLE){
+ MTFace *mt;
+ efa = element->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+
+ preview->preview_stitchable[stitchBufferIndex*2] = mt->uv[element->tfindex][0];
+ preview->preview_stitchable[stitchBufferIndex*2 + 1] = mt->uv[element->tfindex][1];
+ stitchBufferIndex++;
+ }
+ else if(element->flag & STITCH_SELECTED){
+ MTFace *mt;
+ efa = element->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+
+ preview->preview_unstitchable[unstitchBufferIndex*2] = mt->uv[element->tfindex][0];
+ preview->preview_unstitchable[unstitchBufferIndex*2 + 1] = mt->uv[element->tfindex][1];
+ unstitchBufferIndex++;
+ }
+ }
+ }
+
+ /******************************************************
+ * Here we calculate the final coordinates of the uvs *
+ ******************************************************/
+
+ final_position = MEM_callocN(state->selection_size*sizeof(*final_position), "stitch_uv_average");
+ uvfinal_map = MEM_mallocN(state->em->totvert*sizeof(*uvfinal_map), "stitch_uv_final_map");
+
+ /* first pass, calculate final position for stitchable uvs of the static island */
+ for(i = 0; i < state->selection_size; i++){
+ UvElement *element = state->selection_stack[i];
+ if(element->flag & STITCH_STITCHABLE){
+ UvElement *element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l];
+ uvfinal_map[(*(&element->face->v1 + element->tfindex))->tmp.l] = i;
+ for(;element_iter; element_iter = element_iter->next){
+ if(element_iter->flag & STITCH_STITCHABLE){
+ MTFace *mt;
+ efa = element_iter->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+ if(stitch_midpoints){
+ final_position[i].uv[0] += mt->uv[element_iter->tfindex][0];
+ final_position[i].uv[1] += mt->uv[element_iter->tfindex][1];
+ final_position[i].count++;
+ }else if(element_iter->island == state->static_island){
+ final_position[i].uv[0] = mt->uv[element_iter->tfindex][0];
+ final_position[i].uv[1] = mt->uv[element_iter->tfindex][1];
+ }
+ }
+ }
+ }
+ if(stitch_midpoints){
+ final_position[i].uv[0] /= final_position[i].count;
+ final_position[i].uv[1] /= final_position[i].count;
+ }
+ }
+
+ /* second pass, calculate island rotation and translation before modifying any uvs */
+ if(state->snap_islands){
+ for(i = 0; i < state->selection_size; i++){
+ UvElement *element = state->selection_stack[i];
+ if(element->flag & STITCH_STITCHABLE){
+ MTFace *mt;
+ efa = element->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+
+ /* accumulate each islands' translation from stitchable elements. it is important to do here
+ * because in final pass MTFaces get modified and result is zero. */
+ island_stitch_data[element->island].translation[0] += final_position[i].uv[0] - mt->uv[element->tfindex][0];
+ island_stitch_data[element->island].translation[1] += final_position[i].uv[1] - mt->uv[element->tfindex][1];
+ island_stitch_data[element->island].medianPoint[0] += mt->uv[element->tfindex][0];
+ island_stitch_data[element->island].medianPoint[1] += mt->uv[element->tfindex][1];
+ island_stitch_data[element->island].numOfElements++;
+ }
+ }
+
+ /* only calculate rotation when an edge has been fully selected */
+ for(i = 0; i < state->total_boundary_edges; i++){
+ UvEdge *edge = state->edges+i;
+ if((state->uvs[edge->uv1]->flag & STITCH_STITCHABLE) && (state->uvs[edge->uv2]->flag & STITCH_STITCHABLE)){
+ stitch_island_calculate_edge_rotation(edge, state, final_position, uvfinal_map, island_stitch_data);
+ use_vert_normals = 0;
+ }
+ }
+ if(use_vert_normals){
+ for(i = 0; i < state->selection_size; i++){
+ UvElement *element = state->selection_stack[i];
+ if(element->flag & STITCH_STITCHABLE){
+ stitch_island_calculate_vert_rotation(element, state, island_stitch_data, 0);
+ }
+ }
+ }
+ }
+
+ /* third pass, propagate changes to stitchable uvs */
+ for(i = 0; i < state->selection_size; i++){
+ UvElement *element = state->selection_stack[i];
+ if(element->flag & STITCH_STITCHABLE){
+ UvElement *element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l];
+ for(;element_iter;){
+ /* determine if uv stitchable */
+ if(element_iter->separate && element_iter->flag & STITCH_STITCHABLE){
+ MTFace *mt;
+ efa = element_iter->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+
+ /* propagate to coincident uvs */
+ do{
+ efa = element_iter->face;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+
+ element_iter->flag |= STITCH_PROCESSED;
+ /* either flush to preview or to the MTFace, if final */
+ if(final){
+ mt->uv[element_iter->tfindex][0] = final_position[i].uv[0];
+ mt->uv[element_iter->tfindex][1] = final_position[i].uv[1];
+
+ uvedit_uv_select(scene, efa, mt, element_iter->tfindex);
+ }else if(efa->tmp.l != STITCH_NO_PREVIEW){
+ if(efa->v4){
+ *(preview->preview_quads+efa->tmp.l + element_iter->tfindex*2) = final_position[i].uv[0];
+ *(preview->preview_quads+efa->tmp.l + element_iter->tfindex*2 + 1) = final_position[i].uv[1];
+ }else{
+ *(preview->preview_tris+efa->tmp.l + element_iter->tfindex*2) = final_position[i].uv[0];
+ *(preview->preview_tris+efa->tmp.l + element_iter->tfindex*2 + 1) = final_position[i].uv[1];
+ }
+ }
+
+ /* end of calculations, keep only the selection flag */
+ if( (!state->snap_islands) || ((!stitch_midpoints) && (element_iter->island == state->static_island))) {
+ element_iter->flag &= STITCH_SELECTED;
+ }
+
+ element_iter = element_iter->next;
+ }while(element_iter && !element_iter->separate);
+
+ continue;
+ }
+ element_iter = element_iter->next;
+ }
+ }
+ }
+
+ /* final pass, calculate Island translation/rotation if needed */
+ if(state->snap_islands){
+ stitch_calculate_island_snapping(state, preview, island_stitch_data, final);
+ }
+
+ MEM_freeN(final_position);
+ MEM_freeN(uvfinal_map);
+ MEM_freeN(island_stitch_data);
+
+ return 1;
+}
+
+/* Stitch hash initialisation functions */
+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;
+}
+
+
+/* Select all common uvs */
+static void stitch_select_uv(UvElement *element, StitchState *stitch_state, int always_select)
+{
+ /* This works due to setting of tmp in find nearest uv vert */
+ UvElement *element_iter;
+ UvElement **selection_stack = stitch_state->selection_stack;
+
+ element_iter = stitch_state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l];
+ /* first deselect all common uvs */
+ for(; element_iter; element_iter = element_iter->next){
+ if(element_iter->separate){
+ /* only separators go to selection */
+ if(element_iter->flag & STITCH_SELECTED){
+ int i;
+ if(always_select)
+ continue;
+
+ element_iter->flag &= ~STITCH_SELECTED;
+ for(i = 0; i < stitch_state->selection_size; i++){
+ if(selection_stack[i] == element_iter){
+ (stitch_state->selection_size)--;
+ selection_stack[i] = selection_stack[stitch_state->selection_size];
+ break;
+ }
+ }
+ }else{
+ element_iter->flag |= STITCH_SELECTED;
+ selection_stack[(stitch_state->selection_size)++] = element_iter;
+ }
+ }
+ }
+}
+
+static void stitch_calculate_edge_normal(EditMesh *em, UvEdge *edge, float *normal){
+ UvElement *element = edge->element;
+ EditFace *efa = element->face;
+ MTFace *mt = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ int nverts = efa->v4?4 : 3;
+ int index = index = (element->tfindex + 2)%nverts;
+ float tangent[2], internal[2];
+
+ sub_v2_v2v2(tangent, mt->uv[(element->tfindex + 1)%nverts], mt->uv[element->tfindex]);
+ sub_v2_v2v2(internal, mt->uv[index], mt->uv[element->tfindex]);
+
+ /* choose one of the normals */
+ normal[0] = tangent[1];
+ normal[1] = -tangent[0];
+
+ /* if normal points inside the face, invert */
+ if(dot_v2v2(normal, internal) > 0){
+ normal[0] = -tangent[1];
+ normal[1] = tangent[0];
+ }
+
+ normalize_v2(normal);
+}
+
+static int stitch_init(bContext *C, wmOperator *op)
+{
+ /* for fast edge lookup... */
+ GHash *edgeHash;
+ /* ...and actual edge storage */
+ UvEdge *edges;
+ int total_edges;
+ /* maps uvelements to their first coincident uv */
+ int *map;
+ int counter = 0, i;
+ EditFace *efa;
+ EditMesh *em;
+ GHashIterator* ghi;
+ UvEdge *all_edges;
+ StitchState *state = MEM_mallocN(sizeof(StitchState), "stitch state");
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+
+ Object *obedit = CTX_data_edit_object(C);
+
+ op->customdata = state;
+
+ if(!state)
+ return 0;
+
+ /* initialize state */
+ state->use_limit = RNA_boolean_get(op->ptr, "use_limit");
+ state->limit_dist = RNA_float_get(op->ptr, "limit");
+ state->em = em = BKE_mesh_get_editmesh((Mesh*)obedit->data);
+ state->snap_islands = RNA_boolean_get(op->ptr, "snap_islands");
+ state->static_island = RNA_int_get(op->ptr, "static_island");
+ state->midpoints = RNA_boolean_get(op->ptr, "midpoint_snap");
+ /* in uv synch selection, all uv's are visible */
+ if(ts->uv_flag & UV_SYNC_SELECTION){
+ state->element_map = EDBM_make_uv_element_map(state->em, 0, 1);
+ }else{
+ state->element_map = EDBM_make_uv_element_map(state->em, 1, 1);
+ }
+ if(!state->element_map){
+ stitch_state_delete(state);
+ return 0;
+ }
+
+ /* Entirely possible if redoing last operator that static island is bigger than total number of islands.
+ * This ensures we get no hang in the island checking code in stitch_process_data. */
+ state->static_island %= state->element_map->totalIslands;
+
+ /* Count 'unique' uvs */
+ for(i = 0; i < state->element_map->totalUVs; i++){
+ if(state->element_map->buf[i].separate){
+ counter++;
+ }
+ }
+
+ /* Allocate the unique uv buffers */
+ state->uvs = MEM_mallocN(sizeof(*state->uvs)*counter, "uv_stitch_unique_uvs");
+ /* internal uvs need no normals but it is hard and slow to keep a map of
+ * normals only for boundary uvs, so allocating for all uvs */
+ state->normals = MEM_callocN(sizeof(*state->normals)*counter*2, "uv_stitch_normals");
+ state->total_separate_uvs = counter;
+ /* we can at most have totalUVs edges or uvs selected. Actually they are less, considering we store only
+ * unique uvs for processing but I am accounting for all bizarre cases, especially for edges, this way */
+ state->selection_stack = MEM_mallocN(sizeof(*state->selection_stack)*counter, "uv_stitch_selection_stack");
+ state->map = map = MEM_mallocN(sizeof(*map)*state->element_map->totalUVs, "uv_stitch_unique_map");
+ /* Allocate the edge stack */
+ edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash");
+ all_edges = MEM_mallocN(sizeof(*all_edges)*state->element_map->totalUVs, "stitch_all_edges");
+
+ if(!state->selection_stack || !state->uvs || !map || !edgeHash || !all_edges){
+ stitch_state_delete(state);
+ return 0;
+ }
+
+ /* So that we can use this as index for the UvElements */
+ counter = -1;
+ /* initialize the unique UVs and map */
+ for(i = 0; i < state->em->totvert; i++){
+ UvElement *element = state->element_map->vert[i];
+ for(; element; element = element->next){
+ if(element->separate){
+ counter++;
+ state->uvs[counter] = element;
+ }
+ /* pointer arithmetic to the rescue, as always :)*/
+ map[element - state->element_map->buf] = counter;
+ }
+ }
+
+ /* Now, on to generate our uv connectivity data */
+ for(efa = state->em->faces.first, counter = 0; efa; efa = efa->next){
+ if((ts->uv_flag & UV_SYNC_SELECTION) || (!efa->h && efa->f & SELECT)){
+ int nverts = efa->v4 ? 4 : 3;
+
+ for(i = 0; i < nverts; i++){
+ UvElement *element = ED_get_uv_element(state->element_map, efa, i);
+ int offset1, itmp1 = element - state->element_map->buf;
+ int offset2, itmp2 = ED_get_uv_element(state->element_map, efa, (i+1)%nverts) - state->element_map->buf;
+
+ offset1 = map[itmp1];
+ offset2 = map[itmp2];
+
+ all_edges[counter].flag = 0;
+ all_edges[counter].element = element;
+ /* 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){
+ all_edges[counter].uv1 = offset1;
+ all_edges[counter].uv2 = offset2;
+ }
+ else{
+ all_edges[counter].uv1 = offset2;
+ all_edges[counter].uv2 = offset1;
+ }
+
+ if(BLI_ghash_haskey(edgeHash, &all_edges[counter])){
+ char *flag = BLI_ghash_lookup(edgeHash, &all_edges[counter]);
+ *flag = 0;
+ }
+ else{
+ BLI_ghash_insert(edgeHash, &all_edges[counter], &(all_edges[counter].flag));
+ all_edges[counter].flag = STITCH_BOUNDARY;
+ }
+ counter++;
+ }
+ }
+ }
+
+
+ ghi = BLI_ghashIterator_new(edgeHash);
+ total_edges = 0;
+ /* fill the edges with data */
+ for(i = 0; !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){
+ UvEdge *edge = ((UvEdge *)BLI_ghashIterator_getKey(ghi));
+ if(edge->flag & STITCH_BOUNDARY){
+ total_edges++;
+ }
+ }
+ state->edges = edges = MEM_mallocN(sizeof(*edges)*total_edges, "stitch_edges");
+ if(!ghi || !edges){
+ MEM_freeN(all_edges);
+ stitch_state_delete(state);
+ return 0;
+ }
+
+ state->total_boundary_edges = total_edges;
+
+ /* fill the edges with data */
+ for(i = 0, BLI_ghashIterator_init(ghi, edgeHash); !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){
+ UvEdge *edge = ((UvEdge *)BLI_ghashIterator_getKey(ghi));
+ if(edge->flag & STITCH_BOUNDARY){
+ edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi));
+ }
+ }
+
+ /* cleanup temporary stuff */
+ BLI_ghashIterator_free(ghi);
+ MEM_freeN(all_edges);
+
+ /* refill hash with new pointers to cleanup duplicates */
+ BLI_ghash_free(edgeHash, NULL, NULL);
+
+ /***** calculate 2D normals for boundary uvs *****/
+
+ /* we use boundary edges to calculate 2D normals.
+ * to disambiguate the direction of the normal, we also need
+ * a point "inside" the island, that can be provided by
+ * the opposite uv for a quad, or the next uv for a triangle. */
+
+ for(i = 0; i < total_edges; i++){
+ float normal[2];
+ stitch_calculate_edge_normal(em, edges + i, normal);
+
+ add_v2_v2(state->normals + edges[i].uv1*2, normal);
+ add_v2_v2(state->normals + edges[i].uv2*2, normal);
+
+ normalize_v2(state->normals + edges[i].uv1*2);
+ normalize_v2(state->normals + edges[i].uv2*2);
+ }
+
+
+ /***** fill selection stack *******/
+
+ state->selection_size = 0;
+
+ /* Load old selection if redoing operator with different settings */
+ if(RNA_struct_property_is_set(op->ptr, "selection")){
+ int faceIndex, elementIndex;
+ UvElement *element;
+
+ EM_init_index_arrays(em, 0, 0, 1);
+
+
+ RNA_BEGIN(op->ptr, itemptr, "selection") {
+ faceIndex = RNA_int_get(&itemptr, "face_index");
+ elementIndex = RNA_int_get(&itemptr, "element_index");
+ efa = EM_get_face_for_index(faceIndex);
+ element = ED_get_uv_element(state->element_map, efa, elementIndex);
+ stitch_select_uv(element, state, 1);
+ }
+ RNA_END;
+
+ EM_free_index_arrays();
+ /* Clear the selection */
+ RNA_collection_clear(op->ptr, "selection");
+
+ } else {
+ for(efa = state->em->faces.first ; efa; efa = efa->next){
+ int numOfVerts;
+ MTFace *mt;
+ mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE);
+ numOfVerts = efa->v4 ? 4 : 3;
+
+ for(i = 0; i < numOfVerts; i++){
+ if(uvedit_uv_selected(scene, efa, mt, i)){
+ UvElement *element = ED_get_uv_element(state->element_map, efa, i);
+ stitch_select_uv(element, state, 1);
+ }
+ }
+ }
+ }
+
+ /***** initialise static island preview data *****/
+
+ state->quads_per_island = MEM_mallocN(sizeof(*state->quads_per_island)*state->element_map->totalIslands,
+ "stitch island quads");
+ state->tris_per_island = MEM_mallocN(sizeof(*state->tris_per_island)*state->element_map->totalIslands,
+ "stitch island tris");
+ for(i = 0; i < state->element_map->totalIslands; i++){
+ state->quads_per_island[i] = 0;
+ state->tris_per_island[i] = 0;
+ }
+
+ for(efa = state->em->faces.first; efa; efa = efa->next){
+ UvElement *element = ED_get_uv_element(state->element_map, efa, 0);
+
+ if(element){
+ if(efa->v4){
+ state->quads_per_island[element->island]++;
+ }
+ else {
+ state->tris_per_island[element->island]++;
+ }
+ }
+ }
+
+ if(!stitch_process_data(state, scene, 0)){
+ stitch_state_delete(state);
+ return 0;
+ }
+
+ stitch_update_header(state, C);
+ return 1;
+}
+
+static int stitch_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ if(!stitch_init(C, op))
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_modal_handler(C, op);
+ WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void stitch_exit(bContext *C, wmOperator *op, int finished)
+{
+ StitchState *stitch_state;
+ Scene *scene;
+ SpaceImage *sima;
+ ScrArea *sa= CTX_wm_area(C);
+ Object *obedit;
+
+ scene= CTX_data_scene(C);
+ obedit= CTX_data_edit_object(C);
+ sima= CTX_wm_space_image(C);
+
+ stitch_state = (StitchState *)op->customdata;
+
+ if(finished){
+ EditFace *efa;
+ int i;
+
+ RNA_float_set(op->ptr, "limit", stitch_state->limit_dist);
+ RNA_boolean_set(op->ptr, "use_limit", stitch_state->use_limit);
+ RNA_boolean_set(op->ptr, "snap_islands", stitch_state->snap_islands);
+ RNA_int_set(op->ptr, "static_island", stitch_state->static_island);
+ RNA_boolean_set(op->ptr, "midpoint_snap", stitch_state->midpoints);
+
+ for(i = 0, efa = stitch_state->em->faces.first; efa; efa = efa->next, i++){
+ efa->tmp.l = i;
+ }
+
+ /* Store selection for re-execution of stitch */
+ for(i = 0; i < stitch_state->selection_size; i++){
+ PointerRNA itemptr;
+ UvElement *element = stitch_state->selection_stack[i];
+
+ RNA_collection_add(op->ptr, "selection", &itemptr);
+
+ RNA_int_set(&itemptr, "face_index", element->face->tmp.l);
+ RNA_int_set(&itemptr, "element_index", element->tfindex);
+ }
+
+
+ uvedit_live_unwrap_update(sima, scene, obedit);
+ }
+
+ if(sa)
+ ED_area_headerprint(sa, NULL);
+
+ DAG_id_tag_update(obedit->data, 0);
+ WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
+ BKE_mesh_end_editmesh(obedit->data, stitch_state->em);
+
+ stitch_state_delete(stitch_state);
+ op->customdata = NULL;
+
+ stitch_preview_delete();
+}
+
+
+static int stitch_cancel(bContext *C, wmOperator *op)
+{
+ stitch_exit(C, op, 0);
+ return OPERATOR_CANCELLED;
+}
+
+
+static int stitch_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+
+ if(!stitch_init(C, op))
+ return OPERATOR_CANCELLED;
+ if(stitch_process_data((StitchState *)op->customdata, scene, 1)){
+ stitch_exit(C, op, 1);
+ return OPERATOR_FINISHED;
+ }else {
+ return stitch_cancel(C, op);
+ }
+}
+
+static void stitch_select(bContext *C, Scene *scene, wmEvent *event, StitchState *stitch_state){
+ /* add uv under mouse to processed uv's */
+ float co[2];
+ NearestHit hit;
+ ARegion *ar= CTX_wm_region(C);
+ Image *ima= CTX_data_edit_image(C);
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+ uv_find_nearest_vert(scene, ima, stitch_state->em, co, NULL, &hit);
+
+ if(hit.efa)
+ {
+ /* Add vertex to selection, deselect all common uv's of vert other
+ * than selected and update the preview. This behavior was decided so that
+ * you can do stuff like deselect the opposite stitchable vertex and the initial still gets deselected */
+
+ /* This works due to setting of tmp in find nearest uv vert */
+ UvElement *element = ED_get_uv_element(stitch_state->element_map, hit.efa, hit.uv);
+ stitch_select_uv(element, stitch_state, 0);
+
+ }
+}
+
+static int stitch_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ StitchState *stitch_state;
+ Scene *scene = CTX_data_scene(C);
+
+ stitch_state = (StitchState *)op->customdata;
+
+ switch(event->type){
+ case MIDDLEMOUSE:
+ return OPERATOR_PASS_THROUGH;
+
+ /* Cancel */
+ case ESCKEY:
+ return stitch_cancel(C, op);
+
+
+ case LEFTMOUSE:
+ if(event->shift && (U.flag & USER_LMOUSESELECT)){
+ if(event->val == KM_RELEASE){
+ stitch_select(C, scene, event, stitch_state);
+
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ }
+ break;
+ }
+ case PADENTER:
+ case RETKEY:
+ if(stitch_process_data(stitch_state, scene, 1)){
+ stitch_exit(C, op, 1);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return stitch_cancel(C, op);
+ }
+
+ /* Increase limit */
+ case PADPLUSKEY:
+ case WHEELUPMOUSE:
+ if(event->alt){
+ stitch_state->limit_dist += 0.01;
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ break;
+ }
+ else{
+ return OPERATOR_PASS_THROUGH;
+ }
+ /* Decrease limit */
+ case PADMINUS:
+ case WHEELDOWNMOUSE:
+ if(event->alt){
+ stitch_state->limit_dist -= 0.01;
+ stitch_state->limit_dist = MAX2(0.01, stitch_state->limit_dist);
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ break;
+ }else{
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ /* Use Limit (Default off)*/
+ case LKEY:
+ if(event->val == KM_PRESS){
+ stitch_state->use_limit = !stitch_state->use_limit;
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ break;
+ }
+ return OPERATOR_RUNNING_MODAL;
+
+ case IKEY:
+ if(event->val == KM_PRESS){
+ stitch_state->static_island++;
+ stitch_state->static_island %= stitch_state->element_map->totalIslands;
+
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ break;
+ }
+ return OPERATOR_RUNNING_MODAL;
+
+ case MKEY:
+ if(event->val == KM_PRESS){
+ stitch_state->midpoints = !stitch_state->midpoints;
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ }
+ break;
+
+ /* Select geometry*/
+ case RIGHTMOUSE:
+ if(!event->shift){
+ return stitch_cancel(C, op);
+ }
+ if(event->val == KM_RELEASE && !(U.flag & USER_LMOUSESELECT)){
+ stitch_select(C, scene, event, stitch_state);
+
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ break;
+ }
+ return OPERATOR_RUNNING_MODAL;
+
+ /* snap islands on/off */
+ case SKEY:
+ if(event->val == KM_PRESS){
+ stitch_state->snap_islands = !stitch_state->snap_islands;
+ if(!stitch_process_data(stitch_state, scene, 0)){
+ return stitch_cancel(C, op);
+ }
+ break;
+ } else
+ return OPERATOR_RUNNING_MODAL;
+
+ default:
+ return OPERATOR_RUNNING_MODAL;
+ }
+
+ /* if updated settings, renew feedback message */
+ stitch_update_header(stitch_state, C);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void UV_OT_stitch(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Stitch";
+ ot->description = "Stitch selected UV vertices by proximity";
+ ot->idname = "UV_OT_stitch";
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->invoke = stitch_invoke;
+ ot->modal = stitch_modal;
+ ot->exec = stitch_exec;
+ ot->cancel = stitch_cancel;
+ ot->poll= ED_operator_uvedit;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "use_limit", 0, "Use Limit", "Stitch UVs within a specified limit distance");
+ RNA_def_boolean(ot->srna, "snap_islands", 1, "Snap Islands", "Snap islands together. On edge stitch mode, rotates the islands too");
+
+ RNA_def_float(ot->srna, "limit", 0.01f, 0.0f, FLT_MAX, "Limit", "Limit distance in normalized coordinates", 0.0, FLT_MAX);
+ RNA_def_int(ot->srna, "static_island", 0, 0, INT_MAX, "Static Island", "Island that stays in place when stitching islands", 0, INT_MAX);
+ RNA_def_boolean(ot->srna, "midpoint_snap", 0, "Snap At Midpoint", "Uv's are stitched at midpoint instead of at static island");
+ prop = RNA_def_collection_runtime(ot->srna, "selection", &RNA_SelectedUvElement, "Selection", "");
+ /* Selection should not be editable or viewed in toolbar */
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+}
+
+#endif /* BMESH TODO */
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 3ed0cd02838..3377fdad9db 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -40,6 +40,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_modifier_types.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
@@ -50,12 +51,15 @@
#include "BLI_rand.h"
#include "BLI_string.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_subsurf.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_depsgraph.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
+#include "BKE_report.h"
#include "BKE_tessmesh.h"
#include "BLI_math.h"
@@ -318,6 +322,215 @@ static ParamHandle *construct_param_handle(Scene *scene, BMEditMesh *em,
return handle;
}
+#if 0 /* BMESH_TODO */
+static void texface_from_original_index(EditFace *editFace, MTFace *texFace, int index, float **uv, ParamBool *pin, ParamBool *select, Scene *scene)
+{
+ int i, nverts = (editFace->v4)? 4: 3;
+
+ *uv = NULL;
+ *pin = 0;
+ *select = 1;
+
+ if(index == ORIGINDEX_NONE)
+ return;
+
+ for(i = 0; i < nverts; i++) {
+ if((*(&editFace->v1 + i))->tmp.t == index) {
+ *uv = texFace->uv[i];
+ *pin = ((texFace->unwrap & TF_PIN_MASK(i)) != 0);
+ *select = (uvedit_uv_selected(scene, editFace, texFace, i) != 0);
+ }
+ }
+}
+
+/* unwrap handle initialization for subsurf aware-unwrapper. The many modifications required to make the original function(see above)
+ * work justified the existence of a new function. */
+static ParamHandle *construct_param_handle_subsurfed(Scene *scene, EditMesh *editMesh, short fill, short sel, short correct_aspect)
+{
+ ParamHandle *handle;
+ /* index pointers */
+ MFace *face;
+ MEdge *edge;
+ EditVert *editVert;
+ MTFace *texface;
+ EditFace *editFace, **editFaceTmp;
+ EditEdge *editEdge, **editEdgeTmp;
+ int i;
+
+ /* modifier initialization data, will control what type of subdivision will happen*/
+ SubsurfModifierData smd = {{0}};
+ /* Used to hold subsurfed Mesh */
+ DerivedMesh *derivedMesh, *initialDerived;
+ /* holds original indices for subsurfed mesh */
+ int *origVertIndices, *origFaceIndices, *origEdgeIndices;
+ /* Holds vertices of subsurfed mesh */
+ MVert *subsurfedVerts;
+ MEdge *subsurfedEdges;
+ MFace *subsurfedFaces;
+ MTFace *subsurfedTexfaces;
+ /* number of vertices and faces for subsurfed mesh*/
+ int numOfEdges, numOfFaces;
+
+ /* holds a map to editfaces for every subsurfed MFace. These will be used to get hidden/ selected flags etc. */
+ EditFace **faceMap;
+ /* Mini container to hold all EditFaces so that they may be indexed easily and fast. */
+ EditFace **editFaceArray;
+ /* similar to the above, we need a way to map edges to their original ones */
+ EditEdge **edgeMap;
+ EditEdge **editEdgeArray;
+
+ handle = param_construct_begin();
+
+ if(correct_aspect) {
+ EditFace *eface = EM_get_actFace(editMesh, 1);
+
+ if(eface) {
+ float aspx, aspy;
+ texface= CustomData_em_get(&editMesh->fdata, eface->data, CD_MTFACE);
+
+ ED_image_uv_aspect(texface->tpage, &aspx, &aspy);
+
+ if(aspx!=aspy)
+ param_aspect_ratio(handle, aspx, aspy);
+ }
+ }
+
+ /* number of subdivisions to perform */
+ smd.levels = scene->toolsettings->uv_subsurf_level;
+ smd.subdivType = ME_CC_SUBSURF;
+
+ initialDerived = CDDM_from_editmesh(editMesh, NULL);
+ derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd,
+ 0, NULL, 0, 0, 1);
+
+ initialDerived->release(initialDerived);
+
+ /* get the derived data */
+ subsurfedVerts = derivedMesh->getVertArray(derivedMesh);
+ subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh);
+ subsurfedFaces = derivedMesh->getFaceArray(derivedMesh);
+
+ origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX);
+ origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX);
+ origFaceIndices = derivedMesh->getFaceDataArray(derivedMesh, CD_ORIGINDEX);
+
+ subsurfedTexfaces = derivedMesh->getFaceDataArray(derivedMesh, CD_MTFACE);
+
+ numOfEdges = derivedMesh->getNumEdges(derivedMesh);
+ numOfFaces = derivedMesh->getNumFaces(derivedMesh);
+
+ faceMap = MEM_mallocN(numOfFaces*sizeof(EditFace *), "unwrap_edit_face_map");
+ editFaceArray = MEM_mallocN(editMesh->totface*sizeof(EditFace *), "unwrap_editFaceArray");
+
+ /* fill edit face array with edit faces */
+ for(editFace = editMesh->faces.first, editFaceTmp = editFaceArray; editFace; editFace= editFace->next, editFaceTmp++)
+ *editFaceTmp = editFace;
+
+ /* map subsurfed faces to original editFaces */
+ for(i = 0; i < numOfFaces; i++)
+ faceMap[i] = editFaceArray[origFaceIndices[i]];
+
+ MEM_freeN(editFaceArray);
+
+ edgeMap = MEM_mallocN(numOfEdges*sizeof(EditEdge *), "unwrap_edit_edge_map");
+ editEdgeArray = MEM_mallocN(editMesh->totedge*sizeof(EditEdge *), "unwrap_editEdgeArray");
+
+ /* fill edit edge array with edit edges */
+ for(editEdge = editMesh->edges.first, editEdgeTmp = editEdgeArray; editEdge; editEdge= editEdge->next, editEdgeTmp++)
+ *editEdgeTmp = editEdge;
+
+ /* map subsurfed edges to original editEdges */
+ for(i = 0; i < numOfEdges; i++) {
+ /* not all edges correspond to an old edge */
+ edgeMap[i] = (origEdgeIndices[i] != -1)?
+ editEdgeArray[origEdgeIndices[i]] : NULL;
+ }
+
+ MEM_freeN(editEdgeArray);
+
+ /* we need the editvert indices too */
+ for(editVert = editMesh->verts.first, i=0; editVert; editVert = editVert->next, i++)
+ editVert->tmp.t = i;
+
+ /* Prepare and feed faces to the solver */
+ for(i = 0; i < numOfFaces; i++) {
+ ParamKey key, vkeys[4];
+ ParamBool pin[4], select[4];
+ float *co[4];
+ float *uv[4];
+ EditFace *origFace = faceMap[i];
+ MTFace *origtexface = (MTFace *)CustomData_em_get(&editMesh->fdata, origFace->data, CD_MTFACE);
+
+ face = subsurfedFaces+i;
+
+ if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
+ if(origFace->h)
+ continue;
+ }
+ else {
+ if((origFace->h) || (sel && (origFace->f & SELECT)==0))
+ continue;
+ }
+
+ /* Now we feed the rest of the data from the subsurfed faces */
+ texface= subsurfedTexfaces+i;
+
+ /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */
+ key = (ParamKey)face;
+ vkeys[0] = (ParamKey)face->v1;
+ vkeys[1] = (ParamKey)face->v2;
+ vkeys[2] = (ParamKey)face->v3;
+ vkeys[3] = (ParamKey)face->v4;
+
+ co[0] = subsurfedVerts[face->v1].co;
+ co[1] = subsurfedVerts[face->v2].co;
+ co[2] = subsurfedVerts[face->v3].co;
+ co[3] = subsurfedVerts[face->v4].co;
+
+ /* This is where all the magic is done. If the vertex exists in the, we pass the original uv pointer to the solver, thus
+ * flushing the solution to the edit mesh. */
+ texface_from_original_index(origFace, origtexface, origVertIndices[face->v1], &uv[0], &pin[0], &select[0], scene);
+ texface_from_original_index(origFace, origtexface, origVertIndices[face->v2], &uv[1], &pin[1], &select[1], scene);
+ texface_from_original_index(origFace, origtexface, origVertIndices[face->v3], &uv[2], &pin[2], &select[2], scene);
+ texface_from_original_index(origFace, origtexface, origVertIndices[face->v4], &uv[3], &pin[3], &select[3], scene);
+
+ param_face_add(handle, key, 4, vkeys, co, uv, pin, select);
+ }
+
+ /* these are calculated from original mesh too */
+ for(edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) {
+ if((edgeMap[i] != NULL) && edgeMap[i]->seam) {
+ ParamKey vkeys[2];
+ vkeys[0] = (ParamKey)edge->v1;
+ vkeys[1] = (ParamKey)edge->v2;
+ param_edge_set_seam(handle, vkeys);
+ }
+ }
+
+ param_construct_end(handle, fill, 0);
+
+ /* cleanup */
+ MEM_freeN(faceMap);
+ MEM_freeN(edgeMap);
+ derivedMesh->release(derivedMesh);
+
+ return handle;
+}
+
+#else
+
+static ParamHandle *construct_param_handle_subsurfed(Scene *scene, BMEditMesh *editMesh, short fill, short sel, short correct_aspect)
+{
+ (void)scene;
+ (void)editMesh;
+ (void)fill;
+ (void)sel;
+ (void)correct_aspect;
+ return NULL;
+}
+
+#endif /* BMESH_TODO */
+
/* ******************** Minimize Stretch operator **************** */
typedef struct MinStretch {
@@ -619,12 +832,16 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh;
short abf = scene->toolsettings->unwrapper == 0;
short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES;
+ short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF;
if(!ED_uvedit_test(obedit)) {
return;
}
- liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1);
+ if(use_subsurf)
+ liveHandle = construct_param_handle_subsurfed(scene, em, fillholes, 0, 1);
+ else
+ liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1);
param_lscm_begin(liveHandle, PARAM_TRUE, abf);
}
@@ -937,9 +1154,12 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel)
const short fill_holes= scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES;
const short correct_aspect= !(scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT);
- short implicit= 0;
+ const short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF;
- handle= construct_param_handle(scene, em, implicit, fill_holes, sel, correct_aspect);
+ if(use_subsurf)
+ handle = construct_param_handle_subsurfed(scene, em, fill_holes, sel, correct_aspect);
+ else
+ handle= construct_param_handle(scene, em, 0, fill_holes, sel, correct_aspect);
param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
param_lscm_solve(handle);
@@ -960,6 +1180,9 @@ static int unwrap_exec(bContext *C, wmOperator *op)
int method = RNA_enum_get(op->ptr, "method");
int fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
+ int use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data");
+ int subsurf_level = RNA_int_get(op->ptr, "uv_subsurf_level");
+ float obsize[3], unitsize[3] = {1.0f, 1.0f, 1.0f};
short implicit= 0;
if(!uvedit_have_selection(scene, em, implicit)) {
@@ -971,8 +1194,14 @@ static int unwrap_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ mat4_to_size(obsize, obedit->obmat);
+ if(!compare_v3v3(obsize, unitsize, 1e-4f))
+ BKE_report(op->reports, RPT_INFO, "Object scale is not 1.0. Unwrap will operate on a non-scaled version of the mesh.");
+
/* remember last method for live unwrap */
scene->toolsettings->unwrapper = method;
+
+ scene->toolsettings->uv_subsurf_level = subsurf_level;
if(fill_holes) scene->toolsettings->uvcalc_flag |= UVCALC_FILLHOLES;
else scene->toolsettings->uvcalc_flag &= ~UVCALC_FILLHOLES;
@@ -980,6 +1209,9 @@ static int unwrap_exec(bContext *C, wmOperator *op)
if(correct_aspect) scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT;
else scene->toolsettings->uvcalc_flag |= UVCALC_NO_ASPECT_CORRECT;
+ if(use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF;
+ else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF;
+
/* execute unwrap */
ED_unwrap_lscm(scene, obedit, TRUE);
@@ -1013,6 +1245,8 @@ void UV_OT_unwrap(wmOperatorType *ot)
"Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry");
RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect",
"Map UVs taking image aspect ratio into account");
+ RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Data", "Map UV's taking vertex position after subsurf into account");
+ RNA_def_int(ot->srna, "uv_subsurf_level", 1, 1, 6, "SubSurf Target", "Number of times to subdivide before calculating UV's", 1, 6);
}
/**************** Project From View operator **************/