Uv Tools branch GSOC 2011
authorAntony Riakiotakis <kalast@gmail.com>
Tue, 17 Jan 2012 16:31:13 +0000 (16:31 +0000)
committerAntony Riakiotakis <kalast@gmail.com>
Tue, 17 Jan 2012 16:31:13 +0000 (16:31 +0000)
=========================
Documentation: http://wiki.blender.org/index.php/User:Psy-Fi/UV_Tools

Major features include:

*16 bit image support in viewport
*Subsurf aware unwrapping
*Smart Stitch(snap/rotate islands, preview, middlepoint/endpoint stitching)
*Seams from islands tool (marks seams and sharp, depending on settings)
*Uv Sculpting(Grab/Pinch/Rotate)

All tools are complete apart from stitching that is considered stable but with an extra edge mode under development(will be in soc-2011-onion-uv-tools).

42 files changed:
release/scripts/modules/bpy_extras/keyconfig_utils.py
release/scripts/startup/bl_ui/space_image.py
release/scripts/startup/bl_ui/space_userpref.py
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/paint.c
source/blender/blenkernel/intern/scene.c
source/blender/blenlib/BLI_math_vector.h
source/blender/blenlib/intern/math_vector_inline.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/include/ED_image.h
source/blender/editors/include/ED_mesh.h
source/blender/editors/include/ED_uvedit.h
source/blender/editors/include/ED_view3d.h
source/blender/editors/include/UI_resources.h
source/blender/editors/interface/resources.c
source/blender/editors/mesh/editmesh_lib.c
source/blender/editors/object/object_edit.c
source/blender/editors/sculpt_paint/CMakeLists.txt
source/blender/editors/sculpt_paint/SConscript
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/editors/sculpt_paint/paint_ops.c
source/blender/editors/sculpt_paint/sculpt_uv.c [new file with mode: 0644]
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_image/space_image.c
source/blender/editors/uvedit/CMakeLists.txt
source/blender/editors/uvedit/uvedit_draw.c
source/blender/editors/uvedit/uvedit_intern.h
source/blender/editors/uvedit/uvedit_ops.c
source/blender/editors/uvedit/uvedit_parametrizer.c
source/blender/editors/uvedit/uvedit_smart_stitch.c [new file with mode: 0644]
source/blender/editors/uvedit/uvedit_unwrap_ops.c
source/blender/gpu/intern/gpu_draw.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_sculpt_paint.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/intern/wm_keymap.c
source/blenderplayer/bad_level_call_stubs/stubs.c

index a9eb86a..20b0669 100644 (file)
@@ -70,6 +70,7 @@ KM_HIERARCHY = [
     ('Image', 'IMAGE_EDITOR', 'WINDOW', [
         ('UV Editor', 'EMPTY', 'WINDOW', []),  # image (reverse order, UVEdit before Image
         ('Image Paint', 'EMPTY', 'WINDOW', []),  # image and view3d
+        ('UV Sculpt', 'EMPTY', 'WINDOW', []),
         ('Image Generic', 'IMAGE_EDITOR', 'WINDOW', [])
         ]),
 
index ef0e55d..a7999ba 100644 (file)
@@ -52,7 +52,8 @@ class IMAGE_MT_view(Menu):
         layout.prop(sima, "use_realtime_update")
         if show_uvedit:
             layout.prop(toolsettings, "show_uv_local_view")
-            layout.prop(uv, "show_other_objects")
+
+        layout.prop(uv, "show_other_objects")
 
         layout.separator()
 
@@ -146,9 +147,11 @@ class IMAGE_MT_image(Menu):
                     if ima.source in {'FILE', 'GENERATED'} and ima.type != 'OPEN_EXR_MULTILAYER':
                         layout.operator("image.pack", text="Pack As PNG").as_png = True
 
-            layout.separator()
+            if not context.tool_settings.use_uv_sculpt:
+                layout.separator()
+                layout.prop(sima, "use_image_paint")
 
-            layout.prop(sima, "use_image_paint")
+            layout.separator()
 
 
 class IMAGE_MT_image_invert(Menu):
@@ -256,6 +259,10 @@ class IMAGE_MT_uvs(Menu):
 
         layout.separator()
 
+        layout.prop(toolsettings, "use_uv_sculpt")
+
+        layout.separator()
+
         layout.prop(uv, "use_live_unwrap")
         layout.operator("uv.unwrap")
         layout.operator("uv.pin", text="Unpin").clear = True
@@ -267,6 +274,8 @@ class IMAGE_MT_uvs(Menu):
         layout.operator("uv.average_islands_scale")
         layout.operator("uv.minimize_stretch")
         layout.operator("uv.stitch")
+        layout.operator("uv.mark_seam")
+        layout.operator("uv.seams_from_islands")
         layout.operator("mesh.faces_mirror_uv")
 
         layout.separator()
@@ -753,5 +762,80 @@ class IMAGE_PT_paint_curve(BrushButtonsPanel, Panel):
         row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
         row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
 
+
+class IMAGE_UV_sculpt_curve(bpy.types.Panel):
+    bl_space_type = 'IMAGE_EDITOR'
+    bl_region_type = 'UI'
+    bl_label = "UV Sculpt Curve"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    @classmethod
+    def poll(cls, context):
+        sima = context.space_data
+        toolsettings = context.tool_settings.image_paint
+        return sima.show_uvedit and context.tool_settings.use_uv_sculpt and not (sima.show_paint and toolsettings.brush)
+
+    def draw(self, context):
+        layout = self.layout
+
+        toolsettings = context.tool_settings.uv_sculpt
+        brush = toolsettings.brush
+
+        layout.template_curve_mapping(brush, "curve")
+
+        row = layout.row(align=True)
+        row.operator("brush.curve_preset", icon="SMOOTHCURVE", text="").shape = 'SMOOTH'
+        row.operator("brush.curve_preset", icon="SPHERECURVE", text="").shape = 'ROUND'
+        row.operator("brush.curve_preset", icon="ROOTCURVE", text="").shape = 'ROOT'
+        row.operator("brush.curve_preset", icon="SHARPCURVE", text="").shape = 'SHARP'
+        row.operator("brush.curve_preset", icon="LINCURVE", text="").shape = 'LINE'
+        row.operator("brush.curve_preset", icon="NOCURVE", text="").shape = 'MAX'
+
+
+class IMAGE_UV_sculpt(bpy.types.Panel):
+    bl_space_type = 'IMAGE_EDITOR'
+    bl_region_type = 'UI'
+    bl_label = "UV Sculpt"
+
+    @classmethod
+    def poll(cls, context):
+        sima = context.space_data
+        toolsettings = context.tool_settings.image_paint
+        return sima.show_uvedit and context.tool_settings.use_uv_sculpt and not (sima.show_paint and toolsettings.brush)
+
+    def draw(self, context):
+        layout = self.layout
+
+        toolsettings = context.tool_settings.uv_sculpt
+        brush = toolsettings.brush
+
+        if brush:
+            col = layout.column()
+
+            row = col.row(align=True)
+            row.prop(brush, "size", slider=True)
+            row.prop(brush, "use_pressure_size", toggle=True, text="")
+
+            row = col.row(align=True)
+            row.prop(brush, "strength", slider=True)
+            row.prop(brush, "use_pressure_strength", toggle=True, text="")
+
+        split = layout.split()
+        col = split.column()
+
+        col.prop(context.tool_settings, "uv_sculpt_lock_borders");
+        col.prop(context.tool_settings, "uv_sculpt_all_islands");
+        
+        split = layout.split()
+        col = split.column()
+
+        col.prop(context.tool_settings, "uv_sculpt_tool");
+        
+        if context.tool_settings.uv_sculpt_tool == 'RELAX':
+            col.prop(context.tool_settings, "uv_relax_method");
+        
+
+
 if __name__ == "__main__":  # only for live edit.
     bpy.utils.register_module(__name__)
index 98dd780..8a2dff8 100644 (file)
@@ -425,6 +425,7 @@ class USERPREF_PT_system(Panel):
         col.label(text="OpenGL:")
         col.prop(system, "gl_clip_alpha", slider=True)
         col.prop(system, "use_mipmaps")
+        col.prop(system, "use_16bit_textures")
         col.label(text="Anisotropic Filtering")
         col.prop(system, "anisotropic_filter", text="")
         col.prop(system, "use_vertex_buffer_objects")
index 53ff7d8..30a4b15 100644 (file)
@@ -52,7 +52,10 @@ struct CustomData;
 struct DerivedMesh;
 struct Scene;
 struct MLoopUV;
-
+struct UvVertMap;
+struct UvMapVert;
+struct UvElementMap;
+struct UvElement;
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -126,6 +129,39 @@ typedef struct UvMapVert {
        unsigned char tfindex, separate, flag;
 } UvMapVert;
 
+typedef struct UvElementMap {
+       /* address UvElements by their vertex */
+       struct UvElement **vert;
+       /* UvElement Store */
+       struct UvElement *buf;
+       /* Total number of UVs in the layer. Useful to know */
+       int totalUVs;
+       /* Number of Islands in the mesh */
+       int totalIslands;
+       /* Stores the starting index in buf where each island begins */
+       int *islandIndices;
+} UvElementMap;
+
+typedef struct UvElement {
+       /* Next UvElement corresponding to same vertex */
+       struct UvElement *next;
+       /* Face the element belongs to */
+       struct EditFace *face;
+       /* Index in the editFace of the uv */
+       unsigned char tfindex;
+       /* Whether this element is the first of coincident elements */
+       unsigned char separate;
+       /* general use flag */
+       unsigned char flag;
+       /* If generating element map with island sorting, this stores the island index */
+       unsigned short island;
+} UvElement;
+
+/* invalid island index is max short. If any one has the patience
+ * to make that many islands, he can bite me :p */
+#define INVALID_ISLAND 0xFFFF
+
+
 UvVertMap *make_uv_vert_map(struct MFace *mface, struct MTFace *tface, unsigned int totface, unsigned int totvert, int selected, float *limit);
 UvMapVert *get_uv_map_vert(UvVertMap *vmap, unsigned int v);
 void free_uv_vert_map(UvVertMap *vmap);
index fe848f3..2b3f792 100644 (file)
@@ -66,6 +66,11 @@ Paint *paint_get_active(Scene *sce)
                                return &ts->wpaint->paint;
                        case OB_MODE_TEXTURE_PAINT:
                                return &ts->imapaint.paint;
+                       case OB_MODE_EDIT:
+                               if(ts->use_uv_sculpt)
+                                       return &ts->uvsculpt->paint;
+                               else
+                                       return &ts->imapaint.paint;
                        }
                }
 
index 6d37c9c..2816311 100644 (file)
@@ -297,6 +297,10 @@ void free_scene(Scene *sce)
                        free_paint(&sce->toolsettings->sculpt->paint);
                        MEM_freeN(sce->toolsettings->sculpt);
                }
+               if(sce->toolsettings->uvsculpt) {
+                       free_paint(&sce->toolsettings->uvsculpt->paint);
+                       MEM_freeN(sce->toolsettings->uvsculpt);
+               }
                free_paint(&sce->toolsettings->imapaint.paint);
 
                MEM_freeN(sce->toolsettings);
index e9e44ed..b75d8e0 100644 (file)
@@ -116,9 +116,10 @@ MINLINE void star_m3_v3(float rmat[3][3],float a[3]);
 
 /*********************************** Length **********************************/
 
+MINLINE float len_squared_v2(const float v[2]);
 MINLINE float len_v2(const float a[2]);
 MINLINE float len_v2v2(const float a[2], const float b[2]);
-MINLINE float len_squared_v2v2(const float a[3], const float b[3]);
+MINLINE float len_squared_v2v2(const float a[2], const float b[2]);
 MINLINE float len_v3(const float a[3]);
 MINLINE float len_v3v3(const float a[3], const float b[3]);
 MINLINE float len_squared_v3v3(const float a[3], const float b[3]);
index 4570bd5..dc9a322 100644 (file)
@@ -429,6 +429,11 @@ MINLINE void star_m3_v3(float rmat[][3], float a[3])
 
 /*********************************** Length **********************************/
 
+MINLINE float len_squared_v2(const float v[2])
+{
+       return v[0]*v[0] + v[1]*v[1];
+}
+
 MINLINE float len_v2(const float v[2])
 {
        return (float)sqrtf(v[0]*v[0] + v[1]*v[1]);
@@ -448,7 +453,7 @@ MINLINE float len_v3(const float a[3])
        return sqrtf(dot_v3v3(a, a));
 }
 
-MINLINE float len_squared_v2v2(const float a[3], const float b[3])
+MINLINE float len_squared_v2v2(const float a[2], const float b[2])
 {
        float d[2];
 
index 6855ece..fa15590 100644 (file)
@@ -4715,7 +4715,7 @@ static void lib_link_scene(FileData *fd, Main *main)
                        link_paint(fd, sce, &sce->toolsettings->vpaint->paint);
                        link_paint(fd, sce, &sce->toolsettings->wpaint->paint);
                        link_paint(fd, sce, &sce->toolsettings->imapaint.paint);
-
+                       link_paint(fd, sce, &sce->toolsettings->uvsculpt->paint);
                        sce->toolsettings->skgen_template = newlibadr(fd, sce->id.lib, sce->toolsettings->skgen_template);
 
                        for(base= sce->base.first; base; base= next) {
@@ -4845,6 +4845,7 @@ static void direct_link_scene(FileData *fd, Scene *sce)
                direct_link_paint(fd, (Paint**)&sce->toolsettings->sculpt);
                direct_link_paint(fd, (Paint**)&sce->toolsettings->vpaint);
                direct_link_paint(fd, (Paint**)&sce->toolsettings->wpaint);
+               direct_link_paint(fd, (Paint**)&sce->toolsettings->uvsculpt);
 
                sce->toolsettings->imapaint.paintcursor= NULL;
                sce->toolsettings->particle.paintcursor= NULL;
index 7cde4c8..1231a07 100644 (file)
@@ -1963,6 +1963,9 @@ static void write_scenes(WriteData *wd, ListBase *scebase)
                if(tos->sculpt) {
                        writestruct(wd, DATA, "Sculpt", 1, tos->sculpt);
                }
+               if(tos->uvsculpt) {
+                       writestruct(wd, DATA, "UvSculpt", 1, tos->uvsculpt);
+               }
 
                // write_paint(wd, &tos->imapaint.paint);
 
index 957b58b..2058479 100644 (file)
@@ -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);
index 6b37427..790fb88 100644 (file)
@@ -155,6 +155,9 @@ struct UvVertMap *EM_make_uv_vert_map(struct EditMesh *em, int selected, int do_
 struct UvMapVert *EM_get_uv_map_vert(struct UvVertMap *vmap, unsigned int v);
 void              EM_free_uv_vert_map(struct UvVertMap *vmap);
 
+struct UvElementMap *EM_make_uv_element_map(struct EditMesh *em, int selected, int doIslands);
+void           EM_free_uv_element_map(struct UvElementMap *vmap);
+
 void           EM_add_data_layer(struct EditMesh *em, struct CustomData *data, int type, const char *name);
 void           EM_free_data_layer(struct EditMesh *em, struct CustomData *data, int type);
 
index 0666884..bc8a12c 100644 (file)
@@ -81,7 +81,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);
index 2148b0d..f087cdc 100644 (file)
@@ -279,10 +279,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);
 
index 5d95992..852b030 100644 (file)
@@ -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! */
 
index c3fe50e..ad20ff0 100644 (file)
@@ -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;
index 8dea636..5ec147a 100644 (file)
@@ -2322,7 +2322,7 @@ UvVertMap *EM_make_uv_vert_map(EditMesh *em, int selected, int do_face_idx_array
        for(a=0, ev=em->verts.first; ev; a++, ev= ev->next) {
                UvMapVert *newvlist= NULL, *vlist=vmap->vert[a];
                UvMapVert *iterv, *v, *lastv, *next;
-               float *uv, *uv2, uvdiff[2];
+               float *uv, *uv2;
 
                while(vlist) {
                        v= vlist;
@@ -2332,8 +2332,8 @@ UvVertMap *EM_make_uv_vert_map(EditMesh *em, int selected, int do_face_idx_array
 
                        efa = EM_get_face_for_index(v->f);
                        tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
-                       uv = tf->uv[v->tfindex]; 
-                       
+                       uv = tf->uv[v->tfindex];
+
                        lastv= NULL;
                        iterv= vlist;
 
@@ -2342,8 +2342,6 @@ UvVertMap *EM_make_uv_vert_map(EditMesh *em, int selected, int do_face_idx_array
                                efa = EM_get_face_for_index(iterv->f);
                                tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
                                uv2 = tf->uv[iterv->tfindex];
-                               
-                               sub_v2_v2v2(uvdiff, uv2, uv);
 
                                if(fabsf(uv[0]-uv2[0]) < limit[0] && fabsf(uv[1]-uv2[1]) < limit[1]) {
                                        if(lastv) lastv->next= next;
@@ -2353,22 +2351,224 @@ UvVertMap *EM_make_uv_vert_map(EditMesh *em, int selected, int do_face_idx_array
                                }
                                else
                                        lastv=iterv;
-
                                iterv= next;
                        }
-
                        newvlist->separate = 1;
                }
-
                vmap->vert[a]= newvlist;
        }
-       
+
+
        if (do_face_idx_array)
                EM_free_index_arrays();
-       
+
+       return vmap;
+}
+
+/* A specialized vert map used by stitch operator */
+UvElementMap *EM_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) {
+               EM_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);
+                       EM_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;
 }
 
+
 UvMapVert *EM_get_uv_map_vert(UvVertMap *vmap, unsigned int v)
 {
        return vmap->vert[v];
@@ -2383,6 +2583,16 @@ void EM_free_uv_vert_map(UvVertMap *vmap)
        }
 }
 
+void EM_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);
+       }
+}
+
 /* poll call for mesh operators requiring a view3d context */
 int EM_view3d_poll(bContext *C)
 {
index 61ba54e..2dc170a 100644 (file)
@@ -88,7 +88,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"
@@ -513,11 +513,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;
 }
 
index 30f4926..fb20b20 100644 (file)
@@ -27,6 +27,7 @@ set(INC
        ../../imbuf
        ../../makesdna
        ../../makesrna
+       ../uvedit
        ../../render/extern/include
        ../../windowmanager
        ../../../../intern/guardedalloc
@@ -45,6 +46,7 @@ set(SRC
        paint_vertex.c
        sculpt.c
        sculpt_undo.c
+       sculpt_uv.c
 
        paint_intern.h
        sculpt_intern.h
index b3927fc..dd82e01 100644 (file)
@@ -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'
+incs += ' ../../gpu ../../makesrna ../../blenloader ../uvedit'
 
 if env['OURPLATFORM'] == 'linux':
     cflags='-pthread'
index 8eeca2e..fea827e 100644 (file)
@@ -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,8 @@
 #include "BKE_paint.h"
 #include "BKE_report.h"
 #include "BKE_scene.h"
+#include "BKE_global.h"
+#include "BKE_deform.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
@@ -91,6 +94,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"
 #include "RNA_enum_types.h"
 
 #include "GPU_draw.h"
+#include "GPU_extensions.h"
 
 #include "paint_intern.h"
 
@@ -3742,15 +3747,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 +3785,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 +4002,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 +4170,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 +4610,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 +4644,30 @@ static int image_paint_poll(bContext *C)
        return 0;
 }
 
+static int uv_sculpt_brush_poll(bContext *C)
+{
+       EditMesh *em;
+       int ret;
+       Object *obedit = CTX_data_edit_object(C);
+       SpaceImage *sima= CTX_wm_space_image(C);
+       ToolSettings *toolsettings = CTX_data_scene(C)->toolsettings;
+
+       if(!uv_sculpt_brush(C) || !obedit || obedit->type != OB_MESH)
+               return 0;
+
+       em = BKE_mesh_get_editmesh(obedit->data);
+       ret = EM_texFaceCheck(em);
+       BKE_mesh_end_editmesh(obedit->data, 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 +5124,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 +5150,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 = CTX_data_scene(C)->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 +5183,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);
@@ -5178,6 +5228,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 +5570,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));
index 2fc7d56..4ca05e1 100644 (file)
@@ -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 */
 
index d4578e1..0ff0f27 100644 (file)
@@ -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 (file)
index 0000000..229ef10
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ * ***** 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 "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(EditMesh *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){
+                               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);
+                       }
+               }
+       }
+
+       MEM_freeN(tmp_uvdata);
+
+       return;
+}
+
+static void laplacian_relaxation_iteration_uv(EditMesh *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){
+                               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);
+                       }
+               }
+       }
+
+       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);
+       EditMesh *em = BKE_mesh_get_editmesh(obedit->data);
+       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){
+                                       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);
+                               }
+                       }
+               }
+       }
+
+       /*
+        * 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){
+                               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);
+                       }
+               }
+       }
+
+       BKE_mesh_end_editmesh(obedit->data, em);
+}
+
+
+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)
+       {
+               EM_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, EditFace *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");
+       EditMesh *em = BKE_mesh_get_editmesh(obedit->data);
+
+       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 = EM_make_uv_element_map(em, 0, 1);
+                       }else{
+                               data->elementMap = EM_make_uv_element_map(em, 1, 1);
+                       }
+               }else {
+                       if(ts->uv_flag & UV_SYNC_SELECTION){
+                               data->elementMap = EM_make_uv_element_map(em, 0, 0);
+                       }else{
+                               data->elementMap = EM_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.uv);
+                       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 < em->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;
+                                       }
+                                       efa = element->face;
+                                       mt = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+
+                                       counter++;
+                                       data->uv[counter].element = element;
+                                       data->uv[counter].flag = 0;
+                                       data->uv[counter].uv = mt->uv[element->tfindex];
+                               }
+                               /* pointer arithmetic to the rescue, as always :)*/
+                               uniqueUv[element - data->elementMap->buf] = counter;
+                       }
+               }
+
+               /* 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++;
+                       }
+               }
+               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;
+                       unsigned int tool;
+                       UvSculptData *sculptdata = (UvSculptData *)op->customdata;
+                       SpaceImage *sima;
+                       int width, height;
+                       float aspectRatio;
+                       float alpha, zoomx, zoomy;
+                       Brush *brush = paint_brush(sculptdata->uvsculpt);
+                       tool = CTX_data_scene(C)->toolsettings->uv_sculpt_tool;
+
+                       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");
+}
index e7a139f..2a8a5b3 100644 (file)
@@ -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");
index 66a3838..fd78950 100644 (file)
@@ -773,6 +773,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);
@@ -785,6 +788,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;
@@ -810,7 +814,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);
                
index ce078c2..a747c2a 100644 (file)
@@ -39,6 +39,7 @@ set(SRC
        uvedit_draw.c
        uvedit_ops.c
        uvedit_parametrizer.c
+       uvedit_smart_stitch.c
        uvedit_unwrap_ops.c
 
        uvedit_intern.h
index 0da3f66..6ced91f 100644 (file)
@@ -380,12 +380,9 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, EditMesh *em, MTFac
        }
 }
 
-static void draw_uvs_other(Scene *scene, Object *obedit, MTFace *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);
 
@@ -419,6 +416,34 @@ static void draw_uvs_other(Scene *scene, Object *obedit, MTFace *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) {
+               MFace *mface= me->mface;
+               MTFace *tface= me->mtface;
+               int a;
+
+               for(a=me->totface; a>0; a--, tface++, mface++) {
+                       if(tface->tpage == curimage) {
+                               glBegin(GL_LINE_LOOP);
+                               glVertex2fv(tface->uv[0]);
+                               glVertex2fv(tface->uv[1]);
+                               glVertex2fv(tface->uv[2]);
+                               if(mface->v4) glVertex2fv(tface->uv[3]);
+                               glEnd();
+                       }
+               }
+       }
+}
+
 /* draws uv's in the image space */
 static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
 {
@@ -432,6 +457,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
        float pointsize;
        int drawfaces, interpedges;
        Image *ima= sima->image;
+       StitchPreviewer *stitch_preview = uv_get_stitch_previewer();
 
        em= BKE_mesh_get_editmesh(me);
        activetf= EM_get_active_mtface(em, &efa_act, NULL, 0); /* will be set to NULL if hidden */
@@ -445,8 +471,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 */
        
@@ -522,7 +551,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit)
                }
                
        }
-       
+
        /* 3. draw active face stippled */
 
        if(activetf) {
@@ -831,24 +860,81 @@ 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);
        BKE_mesh_end_editmesh(obedit->data, em);
 }
 
-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);
        }
 }
index 7d83f52..ef25159 100644 (file)
 #ifndef ED_UVEDIT_INTERN_H
 #define ED_UVEDIT_INTERN_H
 
-struct SpaceImage;
 struct EditFace;
-struct MTFace;
-struct Scene;
+struct EditMesh;
 struct Image;
+struct MTFace;
 struct Object;
+struct Scene;
+struct SpaceImage;
+struct UvElementMap;
 struct wmOperatorType;
 
 /* id can be from 0 to 3 */
 #define TF_PIN_MASK(id) (TF_PIN1 << id)
 #define TF_SEL_MASK(id) (TF_SEL1 << id)
 
-
 /* 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);
 
+/* find nearest */
+
+typedef struct NearestHit {
+       struct EditFace *efa;
+       struct MTFace *tf;
+
+       int vert, uv;
+       int edge, vert2;
+} NearestHit;
+
+void uv_find_nearest_vert(struct Scene *scene, struct Image *ima, struct EditMesh *em, float co[2], float penalty[2], struct NearestHit *hit);
+void uv_find_nearest_edge(struct Scene *scene, struct Image *ima, struct EditMesh *em, float co[2], struct NearestHit *hit);
+
+/* utility tool functions */
+
+struct UvElement *ED_get_uv_element(struct UvElementMap *map, struct EditFace *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);
@@ -60,6 +109,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 */
 
index 98fef07..612b467 100644 (file)
@@ -20,7 +20,7 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Antony Riakiotakis.
  *
  * ***** END GPL LICENSE BLOCK *****
  */
@@ -94,6 +94,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)
@@ -400,7 +422,7 @@ void uvedit_uv_deselect(Scene *scene, EditFace *efa, MTFace *tf, int i)
 
 /*********************** 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);
@@ -527,15 +549,7 @@ static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent,
 
 /************************** find nearest ****************************/
 
-typedef struct NearestHit {
-       EditFace *efa;
-       MTFace *tf;
-
-       int vert, uv;
-       int edge, vert2;
-} NearestHit;
-
-static void find_nearest_uv_edge(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit)
+void uv_find_nearest_edge(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit)
 {
        MTFace *tf;
        EditFace *efa;
@@ -633,7 +647,7 @@ static int nearest_uv_between(MTFace *tf, int nverts, int id, float co[2], float
        return (c1*c2 >= 0.0f);
 }
 
-static void find_nearest_uv_vert(Scene *scene, Image *ima, EditMesh *em, float co[2], float penalty[2], NearestHit *hit)
+void uv_find_nearest_vert(Scene *scene, Image *ima, EditMesh *em, float co[2], float penalty[2], NearestHit *hit)
 {
        EditFace *efa;
        EditVert *eve;
@@ -750,6 +764,17 @@ static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, EditFace *efa, int a)
        return NULL;
 }
 
+UvElement *ED_get_uv_element(UvElementMap *map, EditFace *efa, int index)
+{
+       UvElement *element = map->vert[(*(&efa->v1 + index))->tmp.l];
+
+       for(; element; element = element->next)
+               if(element->face == efa)
+                       return element;
+
+       return NULL;
+}
+
 static int uv_edge_tag_faces(UvMapVert *first1, UvMapVert *first2, int *totface)
 {
        UvMapVert *iterv1, *iterv2;
@@ -1296,197 +1321,6 @@ static void UV_OT_weld(wmOperatorType *ot)
        ot->poll= ED_operator_uvedit;
 }
 
-/* ******************** stitch operator **************** */
-
-/* just for averaging UVs */
-typedef struct UVVertAverage {
-       float uv[2];
-       int count;
-} UVVertAverage;
-
-static int stitch_exec(bContext *C, wmOperator *op)
-{
-       SpaceImage *sima;
-       Scene *scene;
-       Object *obedit;
-       EditMesh *em;
-       EditFace *efa;
-       EditVert *eve;
-       Image *ima;
-       MTFace *tf;
-
-       scene= CTX_data_scene(C);
-       obedit= CTX_data_edit_object(C);
-       em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
-       ima= CTX_data_edit_image(C);
-       sima= CTX_wm_space_image(C);
-       
-       if(RNA_boolean_get(op->ptr, "use_limit")) {
-               UvVertMap *vmap;
-               UvMapVert *vlist, *iterv;
-               float newuv[2], limit[2];
-               int a, vtot;
-
-               limit[0]= RNA_float_get(op->ptr, "limit");
-               limit[1]= limit[0];
-
-               EM_init_index_arrays(em, 0, 0, 1);
-               vmap= EM_make_uv_vert_map(em, 1, 0, limit);
-
-               if(vmap == NULL) {
-                       BKE_mesh_end_editmesh(obedit->data, em);
-                       return OPERATOR_CANCELLED;
-               }
-
-               for(a=0, eve= em->verts.first; eve; a++, eve= eve->next) {
-                       vlist= EM_get_uv_map_vert(vmap, a);
-
-                       while(vlist) {
-                               newuv[0]= 0; newuv[1]= 0;
-                               vtot= 0;
-
-                               for(iterv=vlist; iterv; iterv=iterv->next) {
-                                       if((iterv != vlist) && iterv->separate)
-                                               break;
-
-                                       efa = EM_get_face_for_index(iterv->f);
-                                       tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
-                                       
-                                       if(uvedit_uv_selected(scene, efa, tf, iterv->tfindex)) {
-                                               newuv[0] += tf->uv[iterv->tfindex][0];
-                                               newuv[1] += tf->uv[iterv->tfindex][1];
-                                               vtot++;
-                                       }
-                               }
-
-                               if(vtot > 1) {
-                                       newuv[0] /= vtot; newuv[1] /= vtot;
-
-                                       for(iterv=vlist; iterv; iterv=iterv->next) {
-                                               if((iterv != vlist) && iterv->separate)
-                                                       break;
-
-                                               efa = EM_get_face_for_index(iterv->f);
-                                               tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
-
-                                               if(uvedit_uv_selected(scene, efa, tf, iterv->tfindex)) {
-                                                       tf->uv[iterv->tfindex][0]= newuv[0];
-                                                       tf->uv[iterv->tfindex][1]= newuv[1];
-                                               }
-                                       }
-                               }
-
-                               vlist= iterv;
-                       }
-               }
-
-               EM_free_uv_vert_map(vmap);
-               EM_free_index_arrays();
-       }
-       else {
-               UVVertAverage *uv_average, *uvav;
-               int count;
-
-               // index and count verts
-               for(count=0, eve=em->verts.first; eve; count++, eve= eve->next)
-                       eve->tmp.l = count;
-               
-               uv_average= MEM_callocN(sizeof(UVVertAverage)*count, "Stitch");
-               
-               // gather uv averages per vert
-               for(efa= em->faces.first; efa; efa= efa->next) {
-                       tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
-
-                       if(uvedit_face_visible(scene, ima, efa, tf)) {
-                               if(uvedit_uv_selected(scene, efa, tf, 0)) {
-                                       uvav = uv_average + efa->v1->tmp.l;
-                                       uvav->count++;
-                                       uvav->uv[0] += tf->uv[0][0];
-                                       uvav->uv[1] += tf->uv[0][1];
-                               }
-
-                               if(uvedit_uv_selected(scene, efa, tf, 1)) {
-                                       uvav = uv_average + efa->v2->tmp.l;
-                                       uvav->count++;
-                                       uvav->uv[0] += tf->uv[1][0];
-                                       uvav->uv[1] += tf->uv[1][1];
-                               }
-
-                               if(uvedit_uv_selected(scene, efa, tf, 2)) {
-                                       uvav = uv_average + efa->v3->tmp.l;
-                                       uvav->count++;
-                                       uvav->uv[0] += tf->uv[2][0];
-                                       uvav->uv[1] += tf->uv[2][1];
-                               }
-
-                               if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) {
-                                       uvav = uv_average + efa->v4->tmp.l;
-                                       uvav->count++;
-                                       uvav->uv[0] += tf->uv[3][0];
-                                       uvav->uv[1] += tf->uv[3][1];
-                               }
-                       }
-               }
-               
-               // apply uv welding
-               for(efa= em->faces.first; efa; efa= efa->next) {
-                       tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
-
-                       if(uvedit_face_visible(scene, ima, efa, tf)) {
-                               if(uvedit_uv_selected(scene, efa, tf, 0)) {
-                                       uvav = uv_average + efa->v1->tmp.l;
-                                       tf->uv[0][0] = uvav->uv[0]/uvav->count;
-                                       tf->uv[0][1] = uvav->uv[1]/uvav->count;
-                               }
-
-                               if(uvedit_uv_selected(scene, efa, tf, 1)) {
-                                       uvav = uv_average + efa->v2->tmp.l;
-                                       tf->uv[1][0] = uvav->uv[0]/uvav->count;
-                                       tf->uv[1][1] = uvav->uv[1]/uvav->count;
-                               }
-
-                               if(uvedit_uv_selected(scene, efa, tf, 2)) {
-                                       uvav = uv_average + efa->v3->tmp.l;
-                                       tf->uv[2][0] = uvav->uv[0]/uvav->count;
-                                       tf->uv[2][1] = uvav->uv[1]/uvav->count;
-                               }
-
-                               if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) {
-                                       uvav = uv_average + efa->v4->tmp.l;
-                                       tf->uv[3][0] = uvav->uv[0]/uvav->count;
-                                       tf->uv[3][1] = uvav->uv[1]/uvav->count;
-                               }
-                       }
-               }
-
-               MEM_freeN(uv_average);
-       }
-
-       uvedit_live_unwrap_update(sima, scene, obedit);
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
-
-       BKE_mesh_end_editmesh(obedit->data, em);
-       return OPERATOR_FINISHED;
-}
-
-static void UV_OT_stitch(wmOperatorType *ot)
-{
-       /* 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->exec= stitch_exec;
-       ot->poll= ED_operator_uvedit;
-
-       /* properties */
-       RNA_def_boolean(ot->srna, "use_limit", 1, "Use Limit", "Stitch UVs within a specified limit distance");
-       RNA_def_float(ot->srna, "limit", 0.01f, 0.0f, FLT_MAX, "Limit", "Limit distance in normalized coordinates", -FLT_MAX, FLT_MAX);
-}
-
 /* ******************** (de)select all operator **************** */
 
 static void select_all_perform(bContext *C, int action)
@@ -1660,7 +1494,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) {
                        BKE_mesh_end_editmesh(obedit->data, em);
                        return OPERATOR_CANCELLED;
@@ -1668,7 +1502,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) {
                        BKE_mesh_end_editmesh(obedit->data, em);
                        return OPERATOR_CANCELLED;
@@ -1683,7 +1517,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) {
                        BKE_mesh_end_editmesh(obedit->data, em);
                        return OPERATOR_CANCELLED;
@@ -1723,7 +1557,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop)
                else hitv[3]= 0xFFFFFFFF;
        }
        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) {
                        BKE_mesh_end_editmesh(obedit->data, em);
@@ -2015,7 +1849,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;
        }
 
@@ -3312,6 +3146,180 @@ 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;
+       EditMesh *em;
+       EditEdge *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");
+
+       em = BKE_mesh_get_editmesh(me);
+
+       if(!EM_texFaceCheck(em)) {
+               BKE_mesh_end_editmesh(ob->data, em);
+               return OPERATOR_CANCELLED;
+       }
+
+       /* This code sets editvert->tmp.l to the index. This will be useful later on. */
+       EM_init_index_arrays(em, 0, 0, 1);
+       vmap = EM_make_uv_vert_map(em, 0, 0, limit);
+
+       for(editedge = em->edges.first; editedge; editedge = editedge->next) {
+               /* 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;
+
+               EditFace *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[editedge->v1->tmp.l];
+               if(mark_seams)
+                       editedge->seam = 0;
+
+               for(mv1 = mvinit1; mv1 && !faces_separated; mv1 = mv1->next) {
+                       if(mv1->separate && commonFaces)
+                               v1coincident = 0;
+
+                       separated2 = 0;
+                       efa1 = EM_get_face_for_index(mv1->f);
+                       mvinit2 = vmap->vert[editedge->v2->tmp.l];
+
+                       for(mv2 = mvinit2; mv2; mv2 = mv2->next) {
+                               if(mv2->separate)
+                                       mv2sep = mv2;
+
+                               efa2 = EM_get_face_for_index(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)
+                               editedge->seam = 1;
+                       if(mark_sharp)
+                               editedge->sharp = 1;
+               }
+       }
+
+       me->drawflag |= ME_DRAWSEAMS;
+
+       EM_free_uv_vert_map(vmap);
+       EM_free_index_arrays();
+       BKE_mesh_end_editmesh(me, 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;
+       EditMesh *em= BKE_mesh_get_editmesh(me);
+       EditFace *efa;
+
+       for(efa = em->faces.first; efa; efa = efa->next) {
+               MTFace *mt = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+               int i, nverts = efa->v4? 4 : 3;
+
+               for(i = 0; i < nverts; i++)
+                       if(uvedit_edge_selected(scene, efa, mt, i))
+                               (*(&efa->e1 + i))->seam = 1;
+       }
+
+       me->drawflag |= ME_DRAWSEAMS;
+
+       if(scene->toolsettings->edge_mode_live_unwrap)
+               ED_unwrap_lscm(scene, ob, FALSE);
+
+       BKE_mesh_end_editmesh(me, 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_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)
@@ -3331,6 +3339,8 @@ void ED_operatortypes_uvedit(void)
 
        WM_operatortype_append(UV_OT_align);
        WM_operatortype_append(UV_OT_stitch);
+       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);
 
@@ -3357,7 +3367,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);
index 7ad5732..d651a17 100644 (file)
@@ -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 (file)
index 0000000..6a9dda3
--- /dev/null
@@ -0,0 +1,1442 @@
+/*
+ * ***** 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 "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){
+                       EM_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 = EM_make_uv_element_map(state->em, 0, 1);
+       }else{
+               state->element_map = EM_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_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);
+}
+
+
index 17e5beb..3ed4df6 100644 (file)
@@ -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_math.h"
 #include "BLI_edgehash.h"
 #include "BLI_utildefines.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 "PIL_time.h"
 
@@ -272,6 +276,201 @@ static ParamHandle *construct_param_handle(Scene *scene, EditMesh *em, short imp
        return handle;
 }
 
+
+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;
+}
+
 /* ******************** Minimize Stretch operator **************** */
 
 typedef struct MinStretch {
@@ -582,13 +781,17 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
        EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
        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)) {
                BKE_mesh_end_editmesh(obedit->data, em);
                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);
        BKE_mesh_end_editmesh(obedit->data, em);
@@ -900,14 +1103,17 @@ static void uv_map_clip_correct(EditMesh *em, wmOperator *op)
 /* assumes UV Map is checked, doesn't run update funcs */
 void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel)
 {
-       EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
        ParamHandle *handle;
 
+       EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data);
        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);
@@ -930,6 +1136,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)) {
@@ -944,8 +1153,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;
@@ -953,6 +1168,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);
 
@@ -986,6 +1204,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 **************/
index 5d36ba1..59802f2 100644 (file)
@@ -410,9 +410,12 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int
        ImBuf *ibuf = NULL;
        unsigned int *bind = NULL;
        int rectw, recth, tpx=0, tpy=0, y;
-       unsigned int *rectrow, *tilerectrow;
        unsigned int *tilerect= NULL, *scalerect= NULL, *rect= NULL;
+       float *ftilerect= NULL, *fscalerect = NULL, *frect = NULL;
+       float *srgb_frect = NULL;
        short texwindx, texwindy, texwinsx, texwinsy;
+       /* flag to determine whether high resolution format is used */
+       int use_high_bit_depth = FALSE, do_color_management = FALSE;
 
        /* initialize tile mode and number of repeats */
        GTS.ima = ima;
@@ -462,9 +465,20 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int
        if(ibuf==NULL)
                return 0;
 
-       /* ensure we have a char buffer and not only float */
-       if ((ibuf->rect==NULL) && ibuf->rect_float)
-               IMB_rect_from_float(ibuf);
+       if(ibuf->rect_float) {
+               if(U.use_16bit_textures) {
+                       /* use high precision textures. This is relatively harmless because OpenGL gives us
+                          a high precision format only if it is available */
+                       use_high_bit_depth = TRUE;
+               }
+
+               /* TODO unneeded when float images are correctly treated as linear always */
+               if(ibuf->profile == IB_PROFILE_LINEAR_RGB)
+                       do_color_management = TRUE;
+
+               if(ibuf->rect==NULL)
+                       IMB_rect_from_float(ibuf);
+       }
 
        /* currently, tpage refresh is used by ima sequences */
        if(ima->tpageflag & IMA_TPAGE_REFRESH) {
@@ -498,17 +512,39 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int
                        tpx= texwindx;
                        tpy= texwindy;
 
-                       rect= ibuf->rect + texwinsy*ibuf->x + texwinsx;
+                       if(use_high_bit_depth) {
+                               if(do_color_management) {
+                                       srgb_frect = MEM_mallocN(ibuf->x*ibuf->y*sizeof(float)*4, "floar_buf_col_cor");
+                                       IMB_buffer_float_from_float(srgb_frect, ibuf->rect_float,
+                                               ibuf->channels, IB_PROFILE_SRGB, ibuf->profile, 0,
+                                               ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+                                       frect= srgb_frect + texwinsy*ibuf->x + texwinsx;
+                               }
+                               else
+                                       frect= ibuf->rect_float + texwinsy*ibuf->x + texwinsx;
+                       }
+                       else
+                               rect= ibuf->rect + texwinsy*ibuf->x + texwinsx;
                }
        }
        else {
                /* regular image mode */
                bind= &ima->bindcode;
-               
+
                if(*bind==0) {
                        tpx= ibuf->x;
                        tpy= ibuf->y;
                        rect= ibuf->rect;
+                       if(use_high_bit_depth) {
+                               if(do_color_management) {
+                                       frect = srgb_frect = MEM_mallocN(ibuf->x*ibuf->y*sizeof(*srgb_frect)*4, "floar_buf_col_cor");
+                                       IMB_buffer_float_from_float(srgb_frect, ibuf->rect_float,
+                                                       ibuf->channels, IB_PROFILE_SRGB, ibuf->profile, 0,
+                                                       ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+                               }
+                               else
+                                       frect= ibuf->rect_float;
+                       }
                }
        }
 
@@ -523,26 +559,57 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int
 
        /* for tiles, copy only part of image into buffer */
        if (GTS.tilemode) {
-               tilerect= MEM_mallocN(rectw*recth*sizeof(*tilerect), "tilerect");
+               if(use_high_bit_depth) {
+                       float *frectrow, *ftilerectrow;
 
-               for (y=0; y<recth; y++) {
-                       rectrow= &rect[y*ibuf->x];
-                       tilerectrow= &tilerect[y*rectw];
-                               
-                       memcpy(tilerectrow, rectrow, tpx*sizeof(*rectrow));
+                       ftilerect= MEM_mallocN(rectw*recth*sizeof(*ftilerect), "tilerect");
+
+                       for (y=0; y<recth; y++) {
+                               frectrow= &frect[y*ibuf->x];
+                               ftilerectrow= &ftilerect[y*rectw];
+
+                               memcpy(ftilerectrow, frectrow, tpx*sizeof(*frectrow));
+                       }
+
+                       frect= ftilerect;
                }
+               else {
+                       unsigned int *rectrow, *tilerectrow;
+
+                       tilerect= MEM_mallocN(rectw*recth*sizeof(*tilerect), "tilerect");
+
+                       for (y=0; y<recth; y++) {
+                               rectrow= &rect[y*ibuf->x];
+                               tilerectrow= &tilerect[y*rectw];
+
+                               memcpy(tilerectrow, rectrow, tpx*sizeof(*rectrow));
+                       }
                        
-               rect= tilerect;
+                       rect= tilerect;
+               }
        }
 
-       /* scale if not a power of two */
+       /* scale if not a power of two. this is not strictly necessary for newer 
+          GPUs (OpenGL version >= 2.0) since they support non-power-of-two-textures */
        if (!is_pow2_limit(rectw) || !is_pow2_limit(recth)) {
                rectw= smaller_pow2_limit(rectw);
                recth= smaller_pow2_limit(recth);
                
-               scalerect= MEM_mallocN(rectw*recth*sizeof(*scalerect), "scalerect");
-               gluScaleImage(GL_RGBA, tpx, tpy, GL_UNSIGNED_BYTE, rect, rectw, recth, GL_UNSIGNED_BYTE, scalerect);
-               rect= scalerect;
+               if(use_high_bit_depth) {
+                       fscalerect= MEM_mallocN(rectw*recth*sizeof(*fscalerect)*4, "fscalerect");
+                       gluScaleImage(GL_RGBA, tpx, tpy, GL_FLOAT, frect, rectw, recth, GL_FLOAT, fscalerect);
+                       /* frect will refer to ibuf->rect_float when not color converting. We don't want to free that */
+                       if(do_color_management)
+                               MEM_freeN(frect);
+
+                       frect = fscalerect;
+               }
+               else {
+                       scalerect= MEM_mallocN(rectw*recth*sizeof(*scalerect), "scalerect");
+                       gluScaleImage(GL_RGBA, tpx, tpy, GL_UNSIGNED_BYTE, rect, rectw, recth, GL_UNSIGNED_BYTE, scalerect);
+
+                       rect= scalerect;
+               }
        }
 
        /* create image */
@@ -550,12 +617,18 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int
        glBindTexture( GL_TEXTURE_2D, *bind);
 
        if (!(gpu_get_mipmap() && mipmap)) {
-               glTexImage2D(GL_TEXTURE_2D, 0,  GL_RGBA,  rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect);
+               if(use_high_bit_depth)
+                       glTexImage2D(GL_TEXTURE_2D, 0,  GL_RGBA16,  rectw, recth, 0, GL_RGBA, GL_FLOAT, frect);                 
+               else
+                       glTexImage2D(GL_TEXTURE_2D, 0,  GL_RGBA,  rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
        }
        else {
-               gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, rectw, recth, GL_RGBA, GL_UNSIGNED_BYTE, rect);
+               if(use_high_bit_depth)
+                       gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA16, rectw, recth, GL_RGBA, GL_FLOAT, frect);
+               else
+                       gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, rectw, recth, GL_RGBA, GL_UNSIGNED_BYTE, rect);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0));
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1));
 
@@ -570,9 +643,14 @@ int GPU_verify_image(Image *ima, ImageUser *iuser, int tftile, int compare, int
        /* clean up */
        if (tilerect)
                MEM_freeN(tilerect);
+       if (ftilerect)
+               MEM_freeN(ftilerect);
        if (scalerect)
                MEM_freeN(scalerect);
-
+       if (fscalerect)
+               MEM_freeN(fscalerect);
+       if (srgb_frect)
+               MEM_freeN(srgb_frect);
        return *bind;
 }
 
@@ -692,23 +770,21 @@ void GPU_paint_update_image(Image *ima, int x, int y, int w, int h, int mipmap)
                glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skip_pixels);
                glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skip_rows);
 
-               if (ibuf->rect_float){
-                       /*This case needs a whole new buffer*/
-                       if(ibuf->rect==NULL) {
-                               IMB_rect_from_float(ibuf);
-                       }
-                       else {
-                               /* Do partial drawing. 'buffer' holds only the changed part. Needed for color corrected result */
-                               float *buffer = (float *)MEM_mallocN(w*h*sizeof(float)*4, "temp_texpaint_float_buf");
-                               IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h);
-                               glBindTexture(GL_TEXTURE_2D, ima->bindcode);
-                               glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
+               /* if color correction is needed, we must update the part that needs updating. */
+               if(ibuf->rect_float && (!U.use_16bit_textures || (ibuf->profile == IB_PROFILE_LINEAR_RGB))) {
+                       float *buffer = MEM_mallocN(w*h*sizeof(float)*4, "temp_texpaint_float_buf");
+                       IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h);
+
+                       glBindTexture(GL_TEXTURE_2D, ima->bindcode);
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
                                        GL_FLOAT, buffer);
-                               MEM_freeN(buffer);
-                               if(ima->tpageflag & IMA_MIPMAP_COMPLETE)
-                                       ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
-                               return;
-                       }
+
+                       MEM_freeN(buffer);
+
+                       if(ima->tpageflag & IMA_MIPMAP_COMPLETE)
+                               ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
+
+                       return;
                }
                
                glBindTexture(GL_TEXTURE_2D, ima->bindcode);
@@ -717,8 +793,12 @@ void GPU_paint_update_image(Image *ima, int x, int y, int w, int h, int mipmap)
                glPixelStorei(GL_UNPACK_SKIP_PIXELS, x);
                glPixelStorei(GL_UNPACK_SKIP_ROWS, y);
 
-               glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
-                       GL_UNSIGNED_BYTE, ibuf->rect);
+               if(ibuf->rect_float)
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
+                               GL_FLOAT, ibuf->rect_float);
+               else
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
+                               GL_UNSIGNED_BYTE, ibuf->rect);
 
                glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
                glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
index 2ac08d5..38fc513 100644 (file)
@@ -671,10 +671,20 @@ typedef struct GameData {
 #define GAME_MAT_MULTITEX      1
 #define GAME_MAT_GLSL          2
 
-/* *************************************************************** */
+/* UV Paint */
+#define UV_SCULPT_LOCK_BORDERS                         1
+#define UV_SCULPT_ALL_ISLANDS                          2
+
+#define UV_SCULPT_TOOL_PINCH                           1
+#define UV_SCULPT_TOOL_RELAX                           2
+#define UV_SCULPT_TOOL_GRAB                                    3
+
+#define UV_SCULPT_TOOL_RELAX_LAPLACIAN 1
+#define UV_SCULPT_TOOL_RELAX_HC                        2
+
 /* Markers */
 
-typedef struct TimeMarker {
+typedef struct TimeMarker {    
        struct TimeMarker *next, *prev;
        int frame;
        char name[64];
@@ -780,6 +790,9 @@ typedef struct Sculpt {
        int pad;
 } Sculpt;
 
+typedef struct UvSculpt {
+       Paint paint;
+} UvSculpt;
 /* ------------------------------------------- */
 /* Vertex Paint */
 
@@ -855,6 +868,7 @@ typedef struct ToolSettings {
        VPaint *vpaint;         /* vertex paint */
        VPaint *wpaint;         /* weight paint */
        Sculpt *sculpt;
+       UvSculpt *uvsculpt;     /* uv smooth */
        
        /* Vertex groups */
        float vgroup_weight;
@@ -894,7 +908,7 @@ typedef struct ToolSettings {
        short uvcalc_mapalign;
        short uvcalc_flag;
        short uv_flag, uv_selectmode;
-       short uv_pad;
+       short uv_subsurf_level;
        
        /* Grease Pencil */
        short gpencil_flags;
@@ -966,10 +980,14 @@ typedef struct ToolSettings {
        char auto_normalize; /*auto normalizing mode in wpaint*/
        char multipaint; /* paint multiple bones in wpaint */
 
+       /* UV painting */
+       int use_uv_sculpt;
+       int uv_sculpt_settings;
+       int uv_sculpt_tool;
+       int uv_relax_method;
        /* XXX: these sculpt_paint_* fields are deprecated, use the
           unified_paint_settings field instead! */
-       short sculpt_paint_settings DNA_DEPRECATED;
-       short pad1;
+       short sculpt_paint_settings DNA_DEPRECATED;     short pad1;
        int sculpt_paint_unified_size DNA_DEPRECATED;
        float sculpt_paint_unified_unprojected_radius DNA_DEPRECATED;
        float sculpt_paint_unified_alpha DNA_DEPRECATED;
@@ -1421,6 +1439,7 @@ typedef enum SculptFlags {
 #define UVCALC_FILLHOLES                       1
 #define UVCALC_NO_ASPECT_CORRECT       2       /* would call this UVCALC_ASPECT_CORRECT, except it should be default with old file */
 #define UVCALC_TRANSFORM_CORRECT       4       /* adjust UV's while transforming to avoid distortion */
+#define UVCALC_USESUBSURF                      8       /* Use mesh data after subsurf to compute UVs*/
 
 /* toolsettings->uv_flag */
 #define UV_SYNC_SELECTION      1
index 2fa785e..44bf117 100644 (file)
@@ -252,7 +252,12 @@ typedef struct ThemeSpace {
        char hpad[7];
        
        char preview_back[4];
-       
+       char preview_stitch_face[4];
+       char preview_stitch_edge[4];
+       char preview_stitch_vert[4];
+       char preview_stitch_stitchable[4];
+       char preview_stitch_unstitchable[4];
+       char preview_stitch_active[4];
 } ThemeSpace;
 
 
@@ -391,7 +396,7 @@ typedef struct UserDef {
        
        short widget_unit;              /* defaults to 20 for 72 DPI setting */
        short anisotropic_filter;
-       /*short pad[3];                 */
+       short use_16bit_textures, pad8;
 
        float ndof_sensitivity; /* overall sensitivity of 3D mouse */
        int ndof_flag;                  /* flags for 3D mouse */
@@ -403,7 +408,7 @@ typedef struct UserDef {
        short autokey_mode;             /* autokeying mode */
        short autokey_flag;             /* flags for autokeying */
        
-       short text_render, pad9[3];             /*options for text rendering*/
+       short text_render, pad9;                /*options for text rendering*/
 
        struct ColorBand coba_weight;   /* from texture.h */
 
index b6e76c6..4551094 100644 (file)
@@ -416,6 +416,7 @@ extern StructRNA RNA_Scopes;
 extern StructRNA RNA_Screen;
 extern StructRNA RNA_ScrewModifier;
 extern StructRNA RNA_Sculpt;
+extern StructRNA RNA_SelectedUvElement;
 extern StructRNA RNA_Sensor;
 extern StructRNA RNA_Sequence;
 extern StructRNA RNA_SequenceColorBalance;
index 9b9bdaf..384453f 100644 (file)
 
 #include "BLI_threads.h"
 
+EnumPropertyItem uv_sculpt_relaxation_items[] = {
+       {UV_SCULPT_TOOL_RELAX_LAPLACIAN, "LAPLACIAN", 0, "Laplacian", "Use Laplacian method for relaxation"},
+       {UV_SCULPT_TOOL_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
+       {0, NULL, 0, NULL, NULL}};
+
+EnumPropertyItem uv_sculpt_tool_items[] = {
+       {UV_SCULPT_TOOL_PINCH, "PINCH", 0, "Pinch", "Pinch UVs"},
+       {UV_SCULPT_TOOL_RELAX, "RELAX", 0, "Relax", "Relax UVs"},
+       {UV_SCULPT_TOOL_GRAB, "GRAB", 0, "Grab", "Grab UVs"},
+       {0, NULL, 0, NULL, NULL}};
+
+
 EnumPropertyItem snap_target_items[] = {
        {SCE_SNAP_TARGET_CLOSEST, "CLOSEST", 0, "Closest", "Snap closest point onto target"},
        {SCE_SNAP_TARGET_CENTER, "CENTER", 0, "Center", "Snap center onto target"},
@@ -267,9 +279,15 @@ EnumPropertyItem image_color_depth_items[] = {
 #include "ED_view3d.h"
 #include "ED_mesh.h"
 #include "ED_keyframing.h"
+#include "ED_image.h"
 
 #include "RE_engine.h"
 
+static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr))
+{
+       ED_space_image_uv_sculpt_update(bmain->wm.first, scene->toolsettings);
+}
+
 static int rna_Scene_object_bases_lookup_string(PointerRNA *ptr, const char *key, PointerRNA *r_ptr)
 {
        Scene *scene= (Scene*)ptr->data;
@@ -1429,10 +1447,38 @@ static void rna_def_tool_settings(BlenderRNA  *brna)
        RNA_def_property_pointer_sdna(prop, NULL, "imapaint");
        RNA_def_property_ui_text(prop, "Image Paint", "");
 
+       prop= RNA_def_property(srna, "uv_sculpt", PROP_POINTER, PROP_NONE);
+       RNA_def_property_pointer_sdna(prop, NULL, "uvsculpt");
+       RNA_def_property_ui_text(prop, "UV Sculpt", "");
+
        prop= RNA_def_property(srna, "particle_edit", PROP_POINTER, PROP_NONE);
        RNA_def_property_pointer_sdna(prop, NULL, "particle");
        RNA_def_property_ui_text(prop, "Particle Edit", "");
 
+       prop= RNA_def_property(srna, "use_uv_sculpt", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "use_uv_sculpt", 1);
+       RNA_def_property_ui_text(prop, "UV Sculpt", "Enable brush for uv sculpting");
+       RNA_def_property_ui_icon(prop, ICON_TPAINT_HLT, 0);
+       RNA_def_property_update(prop, NC_SPACE|ND_SPACE_IMAGE, "rna_SpaceImageEditor_uv_sculpt_update");
+
+       prop= RNA_def_property(srna, "uv_sculpt_lock_borders", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uv_sculpt_settings", UV_SCULPT_LOCK_BORDERS);
+       RNA_def_property_ui_text(prop, "Lock Borders", "Disables editing of boundary edges");
+
+       prop= RNA_def_property(srna, "uv_sculpt_all_islands", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "uv_sculpt_settings", UV_SCULPT_ALL_ISLANDS);
+       RNA_def_property_ui_text(prop, "Sculpt All Islands", "Brush operates on all islands");
+
+       prop= RNA_def_property(srna, "uv_sculpt_tool", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "uv_sculpt_tool");
+       RNA_def_property_enum_items(prop, uv_sculpt_tool_items);
+       RNA_def_property_ui_text(prop, "UV Sculpt Tools", "Select Tools for the UV sculpt brushes");
+
+       prop= RNA_def_property(srna, "uv_relax_method", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "uv_relax_method");
+       RNA_def_property_enum_items(prop, uv_sculpt_relaxation_items);
+       RNA_def_property_ui_text(prop, "Relaxation Method", "Algorithm used for UV relaxation");
+
        /* Transform */
        prop= RNA_def_property(srna, "proportional_edit", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_sdna(prop, NULL, "proportional");
@@ -3842,6 +3888,28 @@ static void rna_def_scene_keying_sets_all(BlenderRNA *brna, PropertyRNA *cprop)
        RNA_def_property_update(prop, NC_SCENE|ND_KEYINGSET, NULL);
 }
 
+/* Runtime property, used to remember uv indices, used only in UV stitch for now.
+ */
+static void rna_def_selected_uv_element(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "SelectedUvElement", "PropertyGroup");
+       RNA_def_struct_ui_text(srna, "Selected Uv Element", "");
+
+       /* store the index to the UV element selected */
+       prop= RNA_def_property(srna, "element_index", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_flag(prop, PROP_IDPROPERTY);
+       RNA_def_property_ui_text(prop, "Element Index", "");
+
+       prop= RNA_def_property(srna, "face_index", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_flag(prop, PROP_IDPROPERTY);
+       RNA_def_property_ui_text(prop, "Face Index", "");
+}
+
+
+
 void RNA_def_scene(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -4175,6 +4243,7 @@ void RNA_def_scene(BlenderRNA *brna)
        rna_def_scene_game_data(brna);
        rna_def_scene_render_layer(brna);
        rna_def_transform_orientation(brna);
+       rna_def_selected_uv_element(brna);
        
        /* Scene API */
        RNA_api_scene(srna);
index cd929d3..c3c84c6 100644 (file)
@@ -289,6 +289,16 @@ static void rna_def_sculpt(BlenderRNA  *brna)
        RNA_def_property_update(prop, NC_OBJECT|ND_DRAW, "rna_Sculpt_update");
 }
 
+
+static void rna_def_uv_sculpt(BlenderRNA  *brna)
+{
+       StructRNA *srna;
+
+       srna= RNA_def_struct(brna, "UvSculpt", "Paint");
+       RNA_def_struct_ui_text(srna, "UV Sculpting", "");
+}
+
+
 /* use for weight paint too */
 static void rna_def_vertex_paint(BlenderRNA *brna)
 {
@@ -548,6 +558,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
 {
        rna_def_paint(brna);
        rna_def_sculpt(brna);
+       rna_def_uv_sculpt(brna);
        rna_def_vertex_paint(brna);
        rna_def_image_paint(brna);
        rna_def_particle_edit(brna);
index 7b3c70a..9de6f73 100644 (file)
@@ -37,6 +37,7 @@
 #include "DNA_userdef_types.h"
 #include "DNA_brush_types.h"
 #include "DNA_view3d_types.h"
+#include "DNA_scene_types.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -147,6 +148,12 @@ static void rna_userdef_gl_texture_limit_update(Main *bmain, Scene *scene, Point
        rna_userdef_update(bmain, scene, ptr);
 }
 
+static void rna_userdef_gl_use_16bit_textures(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+       GPU_free_images();
+       rna_userdef_update(bmain, scene, ptr);
+}
+
 static void rna_userdef_select_mouse_set(PointerRNA *ptr,int value)
 {
        UserDef *userdef = (UserDef*)ptr->data;
@@ -1623,6 +1630,42 @@ static void rna_def_userdef_theme_space_image(BlenderRNA *brna)
        RNA_def_property_array(prop, 4);
        RNA_def_property_ui_text(prop, "Scope region background color", "");
        RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop= RNA_def_property(srna, "preview_stitch_face", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_float_sdna(prop, NULL, "preview_stitch_face");
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Stitch preview face color", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop= RNA_def_property(srna, "preview_stitch_edge", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_float_sdna(prop, NULL, "preview_stitch_edge");
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Stitch preview edge color", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop= RNA_def_property(srna, "preview_stitch_vert", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_float_sdna(prop, NULL, "preview_stitch_vert");
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Stitch preview vertex color", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop= RNA_def_property(srna, "preview_stitch_stitchable", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_float_sdna(prop, NULL, "preview_stitch_stitchable");
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Stitch preview stitchable color", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop= RNA_def_property(srna, "preview_stitch_unstitchable", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_float_sdna(prop, NULL, "preview_stitch_unstitchable");
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Stitch preview unstitchable color", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+       prop= RNA_def_property(srna, "preview_stitch_active", PROP_FLOAT, PROP_COLOR_GAMMA);
+       RNA_def_property_float_sdna(prop, NULL, "preview_stitch_active");
+       RNA_def_property_array(prop, 4);
+       RNA_def_property_ui_text(prop, "Stitch preview active island", "");
+       RNA_def_property_update(prop, 0, "rna_userdef_update");
 }
 
 static void rna_def_userdef_theme_space_seq(BlenderRNA *brna)
@@ -2900,6 +2943,11 @@ static void rna_def_userdef_system(BlenderRNA *brna)
                                 "Scale textures for the 3D View (looks nicer but uses more memory and slows image reloading)");
        RNA_def_property_update(prop, 0, "rna_userdef_mipmap_update");
 
+       prop= RNA_def_property(srna, "use_16bit_textures", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "use_16bit_textures", 1);
+       RNA_def_property_ui_text(prop, "16 Bit Float Textures", "Use 16 bit per component texture for float images.");
+       RNA_def_property_update(prop, 0, "rna_userdef_gl_use_16bit_textures");
+
        prop= RNA_def_property(srna, "use_vertex_buffer_objects", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_negative_sdna(prop, NULL, "gameflags", USER_DISABLE_VBO);
        RNA_def_property_ui_text(prop, "VBOs", "Use Vertex Buffer Objects (or Vertex Arrays, if unsupported) for viewport rendering");
index 24af3ff..661bd90 100644 (file)
@@ -1213,7 +1213,14 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
                km = WM_keymap_find_all(C, "Pose", 0, 0);
        }
        else if (strstr(opname, "SCULPT_OT")) {
-               km = WM_keymap_find_all(C, "Sculpt", 0, 0);
+               switch(CTX_data_mode_enum(C)) {
+                       case OB_MODE_SCULPT:
+                               km = WM_keymap_find_all(C, "Sculpt", 0, 0);
+                               break;
+                       case OB_MODE_EDIT:
+                               km = WM_keymap_find_all(C, "UV Sculpt", 0, 0);
+                               break;
+               }
        }
        else if (strstr(opname, "MBALL_OT")) {
                km = WM_keymap_find_all(C, "Metaball", 0, 0);
index 6488c37..570900b 100644 (file)
@@ -202,6 +202,8 @@ int ED_space_image_show_paint(struct SpaceImage *sima){return 0;}
 void ED_space_image_paint_update(struct wmWindowManager *wm, struct ToolSettings *settings){}
 void ED_space_image_set(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit, struct Image *ima){}
 struct ImBuf *ED_space_image_buffer(struct SpaceImage *sima){return (struct ImBuf *) NULL;}
+void ED_space_image_uv_sculpt_update(struct wmWindowManager *wm, struct ToolSettings *settings){}
+
 void ED_screen_set_scene(struct bContext *C, struct Scene *scene){}
 void ED_space_clip_set(struct bContext *C, struct SpaceClip *sc, struct MovieClip *clip){}