diff options
57 files changed, 4011 insertions, 226 deletions
diff --git a/extern/carve/include/carve/polyline_iter.hpp b/extern/carve/include/carve/polyline_iter.hpp index 5092f9abecd..8501e620fea 100644 --- a/extern/carve/include/carve/polyline_iter.hpp +++ b/extern/carve/include/carve/polyline_iter.hpp @@ -20,6 +20,7 @@ #include <list> #include <iterator> #include <limits> +#include <cstddef> #include <carve/polyline_decl.hpp> diff --git a/intern/cycles/blender/addon/enums.py b/intern/cycles/blender/addon/enums.py index da4c73a5d3b..e1b138def3c 100644 --- a/intern/cycles/blender/addon/enums.py +++ b/intern/cycles/blender/addon/enums.py @@ -21,8 +21,8 @@ from . import engine devices = ( - ("CPU", "CPU", "Use CPU for rendering"), - ("GPU", "GPU Compute", "Use GPU compute device for rendering, configured in user preferences")) + ("CPU", "CPU", "Use CPU for rendering"), + ("GPU", "GPU Compute", "Use GPU compute device for rendering, configured in user preferences")) feature_set = ( ("SUPPORTED", "Supported", "Only use finished and supported features"), diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index d3c06084ad3..70f38fa7e8c 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -149,6 +149,7 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel): sub.prop(cscene, "debug_use_spatial_splits") sub.prop(cscene, "use_cache") + class CyclesRender_PT_layers(CyclesButtonsPanel, Panel): bl_label = "Layers" bl_options = {'DEFAULT_CLOSED'} @@ -708,7 +709,7 @@ def draw_device(self, context): scene = context.scene layout = self.layout - if scene.render.engine == "CYCLES": + if scene.render.engine == 'CYCLES': cscene = scene.cycles layout.prop(cscene, "feature_set") @@ -719,6 +720,7 @@ def draw_device(self, context): elif device_type == 'OPENCL' and cscene.feature_set == 'EXPERIMENTAL': layout.prop(cscene, "device") + def draw_pause(self, context): layout = self.layout scene = context.scene @@ -726,7 +728,7 @@ def draw_pause(self, context): if scene.render.engine == "CYCLES": view = context.space_data - if view.viewport_shade == "RENDERED": + if view.viewport_shade == 'RENDERED': cscene = scene.cycles layout.prop(cscene, "preview_pause", icon="PAUSE", text="") diff --git a/release/scripts/modules/bpy_extras/keyconfig_utils.py b/release/scripts/modules/bpy_extras/keyconfig_utils.py index a9eb86aaf79..20b0669e531 100644 --- a/release/scripts/modules/bpy_extras/keyconfig_utils.py +++ b/release/scripts/modules/bpy_extras/keyconfig_utils.py @@ -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', []) ]), diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py index 634c74178fa..c39d904bf08 100644 --- a/release/scripts/modules/rna_xml.py +++ b/release/scripts/modules/rna_xml.py @@ -141,7 +141,7 @@ def rna2xml(fw=print_ln, return number_to_str(s, subsubvalue_type) else: return " ".join([str_recursive(si) for si in s]) - + array_value = " ".join(str_recursive(v) for v in subvalue_rna) node_attrs.append("%s=\"%s\"" % (prop, array_value)) @@ -308,13 +308,13 @@ def xml2rna(root_xml, rna2xml_node(root_xml, root_rna) - # ----------------------------------------------------------------------------- # Utility function used by presets. # The idea is you can run a preset like a script with a few args. # # This roughly matches the operator 'bpy.ops.script.python_file_run' + def _get_context_val(context, path): path_full = "context." + path try: @@ -328,6 +328,7 @@ def _get_context_val(context, path): return value + def xml_file_run(context, filepath, rna_map): import xml.dom.minidom diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py index 28628a48a0b..ac82ffbf48f 100644 --- a/release/scripts/startup/bl_operators/clip.py +++ b/release/scripts/startup/bl_operators/clip.py @@ -76,6 +76,7 @@ def CLIP_camera_for_clip(context, clip): return camera + def CLIP_track_view_selected(sc, track): if track.select_anchor: return True @@ -118,7 +119,7 @@ class CLIP_OT_track_to_empty(Operator): constraint.track = track.name constraint.use_3d_position = False constraint.object = tracking_object.name - constraint.camera = CLIP_camera_for_clip(context, clip); + constraint.camera = CLIP_camera_for_clip(context, clip) def execute(self, context): sc = context.space_data @@ -127,7 +128,7 @@ class CLIP_OT_track_to_empty(Operator): for track in tracking_object.tracks: if CLIP_track_view_selected(sc, track): - self._link_track(context, clip, tracking_object ,track) + self._link_track(context, clip, tracking_object, track) return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 970245fbf87..e9cb8af1cbd 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -166,9 +166,10 @@ class BRUSH_OT_active_index_set(Operator): if attr is None: return {'CANCELLED'} + toolsettings = context.tool_settings for i, brush in enumerate((cur for cur in bpy.data.brushes if getattr(cur, attr))): if i == self.index: - getattr(context.tool_settings, self.mode).brush = brush + getattr(toolsettings, self.mode).brush = brush return {'FINISHED'} return {'CANCELLED'} @@ -1768,4 +1769,3 @@ class WM_OT_addon_expand(Operator): info = addon_utils.module_bl_info(mod) info["show_expanded"] = not info["show_expanded"] return {'FINISHED'} - diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index f0f964f9a31..9f2b3367feb 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -287,7 +287,7 @@ class RENDER_PT_game_player(RenderButtonsPanel, Panel): col = row.column() col.prop(gs, "use_desktop") col.active = gs.show_fullscreen - + col = layout.column() col.label(text="Quality:") col.prop(gs, "samples") diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 895c3611d86..fea7b9673ec 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -418,9 +418,9 @@ class RENDER_PT_stamp(RenderButtonsPanel, Panel): rd = context.scene.render layout.active = rd.use_stamp - + layout.prop(rd, "stamp_font_size", text="Font Size") - + row = layout.row() row.column().prop(rd, "stamp_foreground", slider=True) row.column().prop(rd, "stamp_background", slider=True) @@ -433,7 +433,7 @@ class RENDER_PT_stamp(RenderButtonsPanel, Panel): col.prop(rd, "use_stamp_render_time", text="RenderTime") col.prop(rd, "use_stamp_frame", text="Frame") col.prop(rd, "use_stamp_scene", text="Scene") - + col = split.column() col.prop(rd, "use_stamp_camera", text="Camera") col.prop(rd, "use_stamp_lens", text="Lens") diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 0cf3a921167..b5e23f146b1 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -162,22 +162,25 @@ class CLIP_PT_tools_tracking(Panel): def draw(self, context): layout = self.layout - # clip = context.space_data.clip # UNUSED row = layout.row(align=True) - row.operator("clip.track_markers", text="", icon='FRAME_PREV').backwards = True + props = row.operator("clip.track_markers", text="", icon='FRAME_PREV') + props.backwards = True props = row.operator("clip.track_markers", text="", icon='PLAY_REVERSE') props.backwards = True props.sequence = True - row.operator("clip.track_markers", text="", icon='PLAY').sequence = True + props = row.operator("clip.track_markers", text="", icon='PLAY') + props.sequence = True row.operator("clip.track_markers", text="", icon='FRAME_NEXT') col = layout.column(align=True) - col.operator("clip.clear_track_path", text="Clear After").action = 'REMAINED' + props = col.operator("clip.clear_track_path", text="Clear After") + props.action = 'REMAINED' - col.operator("clip.clear_track_path", text="Clear Before").action = 'UPTO' + props = col.operator("clip.clear_track_path", text="Clear Before") + props.action = 'UPTO' col.operator("clip.clear_track_path", text="Clear").action = 'ALL' layout.operator("clip.join_tracks", text="Join") @@ -371,7 +374,7 @@ class CLIP_PT_objects(Panel): def draw(self, context): layout = self.layout - + sc = context.space_data tracking = sc.clip.tracking @@ -478,7 +481,8 @@ class CLIP_PT_tracking_camera(Panel): label = bpy.types.CLIP_MT_camera_presets.bl_label row.menu('CLIP_MT_camera_presets', text=label) row.operator("clip.camera_preset_add", text="", icon='ZOOMIN') - row.operator("clip.camera_preset_add", text="", icon='ZOOMOUT').remove_active = True + props = row.operator("clip.camera_preset_add", text="", icon='ZOOMOUT') + props.remove_active = True row = layout.row(align=True) sub = row.split(percentage=0.65) @@ -942,7 +946,8 @@ class CLIP_MT_select(Menu): layout.separator() - layout.operator("clip.select_all", text="Select/Deselect all").action = 'TOGGLE' + props = layout.operator("clip.select_all", text="Select/Deselect all") + props.action = 'TOGGLE' layout.operator("clip.select_all", text="Inverse").action = 'INVERT' layout.menu("CLIP_MT_select_grouped") @@ -967,8 +972,12 @@ class CLIP_MT_tracking_specials(Menu): def draw(self, context): layout = self.layout - layout.operator("clip.disable_markers", text="Enable Markers").action = 'ENABLE' - layout.operator("clip.disable_markers", text="Disable markers").action = 'DISABLE' + props = layout.operator("clip.disable_markers", + text="Enable Markers") + props.action = 'ENABLE' + + props = layout.operator("clip.disable_markers", text="Disable markers") + props.action = 'DISABLE' layout.separator() layout.operator("clip.set_origin") diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index ef0e55d74e4..625c3b1ab2f 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -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() @@ -697,8 +706,8 @@ class IMAGE_PT_tools_brush_tool(BrushButtonsPanel, Panel): def draw(self, context): layout = self.layout - settings = context.tool_settings.image_paint - brush = settings.brush + toolsettings = context.tool_settings.image_paint + brush = toolsettings.brush layout.prop(brush, "image_tool", text="") @@ -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 + uvsculpt = toolsettings.uv_sculpt + brush = uvsculpt.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 + uvsculpt = toolsettings.uv_sculpt + brush = uvsculpt.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(toolsettings, "uv_sculpt_lock_borders") + col.prop(toolsettings, "uv_sculpt_all_islands") + + split = layout.split() + col = split.column() + + col.prop(toolsettings, "uv_sculpt_tool") + + if toolsettings.uv_sculpt_tool == 'RELAX': + col.prop(toolsettings, "uv_relax_method") + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index bb0e8cfdae5..7720fddb084 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -28,7 +28,7 @@ class TIME_HT_header(Header): layout = self.layout scene = context.scene - tools = context.tool_settings + toolsettings = context.tool_settings screen = context.screen row = layout.row(align=True) @@ -80,11 +80,11 @@ class TIME_HT_header(Header): layout.separator() row = layout.row(align=True) - row.prop(tools, "use_keyframe_insert_auto", text="", toggle=True) - row.prop(tools, "use_keyframe_insert_keyingset", text="", toggle=True) - if screen.is_animation_playing and tools.use_keyframe_insert_auto: + row.prop(toolsettings, "use_keyframe_insert_auto", text="", toggle=True) + row.prop(toolsettings, "use_keyframe_insert_keyingset", text="", toggle=True) + if screen.is_animation_playing and toolsettings.use_keyframe_insert_auto: subsub = row.row() - subsub.prop(tools, "use_record_with_nla", toggle=True) + subsub.prop(toolsettings, "use_record_with_nla", toggle=True) row = layout.row(align=True) row.prop_search(scene.keying_sets_all, "active", scene, "keying_sets_all", text="") @@ -193,10 +193,10 @@ class TIME_MT_autokey(Menu): def draw(self, context): layout = self.layout - tools = context.tool_settings + toolsettings = context.tool_settings - layout.prop_enum(tools, "auto_keying_mode", 'ADD_REPLACE_KEYS') - layout.prop_enum(tools, "auto_keying_mode", 'REPLACE_KEYS') + layout.prop_enum(toolsettings, "auto_keying_mode", 'ADD_REPLACE_KEYS') + layout.prop_enum(toolsettings, "auto_keying_mode", 'REPLACE_KEYS') def marker_menu_generic(layout): diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 98dd7802f71..8a2dff8f050 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -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") diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 8fd54c9d49a..3aa9434073d 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -760,7 +760,7 @@ class VIEW3D_MT_object_clear(Menu): layout.operator("object.location_clear", text="Location") layout.operator("object.rotation_clear", text="Rotation") layout.operator("object.scale_clear", text="Scale") - layout ("object.origin_clear", text="Origin") + layout.operator("object.origin_clear", text="Origin") class VIEW3D_MT_object_specials(Menu): @@ -1109,9 +1109,9 @@ class VIEW3D_MT_sculpt(Menu): def draw(self, context): layout = self.layout - tool_settings = context.tool_settings - sculpt = tool_settings.sculpt - brush = tool_settings.sculpt.brush + toolsettings = context.tool_settings + sculpt = toolsettings.sculpt + brush = toolsettings.sculpt.brush layout.operator("ed.undo") layout.operator("ed.redo") @@ -1147,8 +1147,8 @@ class VIEW3D_MT_sculpt(Menu): layout.prop(sculpt, "show_brush") # TODO, make available from paint menu! - layout.prop(tool_settings, "sculpt_paint_use_unified_size", text="Unify Size") - layout.prop(tool_settings, "sculpt_paint_use_unified_strength", text="Unify Strength") + layout.prop(toolsettings, "sculpt_paint_use_unified_size", text="Unify Size") + layout.prop(toolsettings, "sculpt_paint_use_unified_strength", text="Unify Strength") # ********** Particle menu ********** @@ -1452,7 +1452,7 @@ class VIEW3D_MT_edit_mesh(Menu): def draw(self, context): layout = self.layout - settings = context.tool_settings + toolsettings = context.tool_settings layout.operator("ed.undo") layout.operator("ed.redo") @@ -1484,9 +1484,9 @@ class VIEW3D_MT_edit_mesh(Menu): layout.separator() - layout.prop(settings, "use_mesh_automerge") - layout.prop_menu_enum(settings, "proportional_edit") - layout.prop_menu_enum(settings, "proportional_edit_falloff") + layout.prop(toolsettings, "use_mesh_automerge") + layout.prop_menu_enum(toolsettings, "proportional_edit") + layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") layout.separator() @@ -1719,7 +1719,7 @@ class VIEW3D_MT_edit_mesh_showhide(ShowHideMenu, Menu): def draw_curve(self, context): layout = self.layout - settings = context.tool_settings + toolsettings = context.tool_settings layout.menu("VIEW3D_MT_transform") layout.menu("VIEW3D_MT_mirror") @@ -1741,8 +1741,8 @@ def draw_curve(self, context): layout.separator() - layout.prop_menu_enum(settings, "proportional_edit") - layout.prop_menu_enum(settings, "proportional_edit_falloff") + layout.prop_menu_enum(toolsettings, "proportional_edit") + layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") layout.separator() @@ -1871,7 +1871,7 @@ class VIEW3D_MT_edit_meta(Menu): def draw(self, context): layout = self.layout - settings = context.tool_settings + toolsettings = context.tool_settings layout.operator("ed.undo") layout.operator("ed.redo") @@ -1890,8 +1890,8 @@ class VIEW3D_MT_edit_meta(Menu): layout.separator() - layout.prop_menu_enum(settings, "proportional_edit") - layout.prop_menu_enum(settings, "proportional_edit_falloff") + layout.prop_menu_enum(toolsettings, "proportional_edit") + layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") layout.separator() @@ -1915,7 +1915,7 @@ class VIEW3D_MT_edit_lattice(Menu): def draw(self, context): layout = self.layout - settings = context.tool_settings + toolsettings = context.tool_settings layout.menu("VIEW3D_MT_transform") layout.menu("VIEW3D_MT_mirror") @@ -1927,8 +1927,8 @@ class VIEW3D_MT_edit_lattice(Menu): layout.separator() - layout.prop_menu_enum(settings, "proportional_edit") - layout.prop_menu_enum(settings, "proportional_edit_falloff") + layout.prop_menu_enum(toolsettings, "proportional_edit") + layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") class VIEW3D_MT_edit_armature(Menu): diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 248452a0e8c..95af67b9d3a 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -453,18 +453,18 @@ class PaintPanel(): @staticmethod def paint_settings(context): - ts = context.tool_settings + toolsettings = context.tool_settings if context.sculpt_object: - return ts.sculpt + return toolsettings.sculpt elif context.vertex_paint_object: - return ts.vertex_paint + return toolsettings.vertex_paint elif context.weight_paint_object: - return ts.weight_paint + return toolsettings.weight_paint elif context.image_paint_object: - return ts.image_paint + return toolsettings.image_paint elif context.particle_edit_object: - return ts.particle_edit + return toolsettings.particle_edit return None @@ -487,6 +487,7 @@ class PaintPanel(): ptr = ups if ups.use_unified_strength else brush parent.prop(ptr, prop_name, icon=icon, text=text, slider=slider) + class VIEW3D_PT_tools_brush(PaintPanel, Panel): bl_label = "Brush" @@ -497,6 +498,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, Panel): def draw(self, context): layout = self.layout + toolsettings = context.tool_settings settings = self.paint_settings(context) brush = settings.brush @@ -541,7 +543,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, Panel): row = col.row(align=True) - ups = context.tool_settings.unified_paint_settings + ups = toolsettings.unified_paint_settings if ((ups.use_unified_size and ups.use_locked_size) or ((not ups.use_unified_size) and brush.use_locked_size)): self.prop_unified_size(row, context, brush, "use_locked_size", icon='LOCKED') @@ -675,9 +677,9 @@ class VIEW3D_PT_tools_brush(PaintPanel, Panel): # Weight Paint Mode # elif context.weight_paint_object and brush: - layout.prop(context.tool_settings, "vertex_group_weight", text="Weight", slider=True) - layout.prop(context.tool_settings, "use_auto_normalize", text="Auto Normalize") - layout.prop(context.tool_settings, "use_multipaint", text="Multi-Paint") + layout.prop(toolsettings, "vertex_group_weight", text="Weight", slider=True) + layout.prop(toolsettings, "use_auto_normalize", text="Auto Normalize") + layout.prop(toolsettings, "use_multipaint", text="Multi-Paint") col = layout.column() @@ -956,8 +958,8 @@ class VIEW3D_PT_sculpt_options(PaintPanel, Panel): def draw(self, context): layout = self.layout - tool_settings = context.tool_settings - sculpt = tool_settings.sculpt + toolsettings = context.tool_settings + sculpt = toolsettings.sculpt layout.label(text="Lock:") row = layout.row(align=True) @@ -1003,11 +1005,11 @@ class VIEW3D_PT_tools_brush_appearance(PaintPanel, Panel): @classmethod def poll(cls, context): - ts = context.tool_settings - return ((context.sculpt_object and ts.sculpt) or - (context.vertex_paint_object and ts.vertex_paint) or - (context.weight_paint_object and ts.weight_paint) or - (context.image_paint_object and ts.image_paint)) + toolsettings = context.tool_settings + return ((context.sculpt_object and toolsettings.sculpt) or + (context.vertex_paint_object and toolsettings.vertex_paint) or + (context.weight_paint_object and toolsettings.weight_paint) or + (context.image_paint_object and toolsettings.image_paint)) def draw(self, context): layout = self.layout @@ -1102,8 +1104,8 @@ class VIEW3D_PT_tools_vertexpaint(PaintPanel, Panel): def draw(self, context): layout = self.layout - tool_settings = context.tool_settings - vpaint = tool_settings.vertex_paint + toolsettings = context.tool_settings + vpaint = toolsettings.vertex_paint col = layout.column() #col.prop(vpaint, "mode", text="") @@ -1141,8 +1143,9 @@ class VIEW3D_PT_tools_projectpaint(View3DPanel, Panel): ob = context.active_object mesh = ob.data - ipaint = context.tool_settings.image_paint - settings = context.tool_settings.image_paint + toolsettings = context.tool_settings + ipaint = toolsettings.image_paint + settings = toolsettings.image_paint use_projection = ipaint.use_projection col = layout.column() @@ -1199,8 +1202,6 @@ class VIEW3D_PT_imagepaint_options(PaintPanel): def draw(self, context): layout = self.layout - tool_settings = context.tool_settings - col = layout.column() self.unified_paint_settings(col, context) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 22cd822bf93..06e4787c741 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -53,7 +53,10 @@ struct CustomData; struct DerivedMesh; struct Scene; struct MLoopUV; - +struct UvVertMap; +struct UvMapVert; +struct UvElementMap; +struct UvElement; #ifdef __cplusplus extern "C" { #endif @@ -180,6 +183,38 @@ 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 BMFace *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 MPoly *mpoly, struct MLoop *mloop, struct MLoopUV *mloopuv, unsigned int totpoly, unsigned int totvert, int selected, float *limit); UvMapVert *get_uv_map_vert(UvVertMap *vmap, unsigned int v); void free_uv_vert_map(UvVertMap *vmap); diff --git a/source/blender/blenkernel/BKE_text.h b/source/blender/blenkernel/BKE_text.h index ffea8e0c8e2..f0c054560c4 100644 --- a/source/blender/blenkernel/BKE_text.h +++ b/source/blender/blenkernel/BKE_text.h @@ -46,8 +46,8 @@ void free_text (struct Text *text); void txt_set_undostate (int u); int txt_get_undostate (void); struct Text* add_empty_text (const char *name); -int txt_extended_ascii_as_utf8(char **str); -int reopen_text (struct Text *text); +int txt_extended_ascii_as_utf8(char **str); +int reopen_text (struct Text *text); struct Text* add_text (const char *file, const char *relpath); struct Text* copy_text (struct Text *ta); void unlink_text (struct Main *bmain, struct Text *text); @@ -60,8 +60,8 @@ void txt_order_cursors (struct Text *text); int txt_find_string (struct Text *text, const char *findstr, int wrap, int match_case); int txt_has_sel (struct Text *text); int txt_get_span (struct TextLine *from, struct TextLine *to); -int txt_utf8_offset_to_index(char *str, int offset); -int txt_utf8_index_to_offset(char *str, int index); +int txt_utf8_offset_to_index(char *str, int offset); +int txt_utf8_index_to_offset(char *str, int index); void txt_move_up (struct Text *text, short sel); void txt_move_down (struct Text *text, short sel); void txt_move_left (struct Text *text, short sel); @@ -162,8 +162,8 @@ int text_check_whitespace(char ch); * by 4 character length ID + the text * block itself + the 4 character length * ID (repeat) and opcode (repeat)) */ -#define UNDO_DBLOCK 027 /* Delete block */ -#define UNDO_IBLOCK 030 /* Insert block */ +#define UNDO_DBLOCK 027 /* Delete block */ +#define UNDO_IBLOCK 030 /* Insert block */ /* Misc */ #define UNDO_SWAP 031 /* Swap cursors */ diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index fe848f3d76c..2b3f792f777 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -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; } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6d37c9ca04c..281631168cd 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -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); diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index bc86a53c35e..40d439169a3 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -226,7 +226,7 @@ int txt_extended_ascii_as_utf8(char **str) while ((*str)[i]) { if((bad_char= BLI_utf8_invalid_byte(*str+i, length)) == -1) - break; + break; added++; i+= bad_char + 1; diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index c2cb4582280..12a0b1892a1 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -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]); diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 6f014a859db..9c5d8f3261f 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -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]; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 978cb4d00e3..b51133d284b 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4794,7 +4794,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) { @@ -4924,6 +4924,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; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index f41e9c91c8f..fb484cc6806 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2077,6 +2077,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); diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 957b58b141c..2058479bed8 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -53,6 +53,7 @@ void ED_space_image_zoom(struct SpaceImage *sima, struct ARegion *ar, float *zoo void ED_space_image_uv_aspect(struct SpaceImage *sima, float *aspx, float *aspy); void ED_space_image_paint_update(struct wmWindowManager *wm, struct ToolSettings *settings); +void ED_space_image_uv_sculpt_update(struct wmWindowManager *wm, struct ToolSettings *settings); void ED_image_size(struct Image *ima, int *width, int *height); void ED_image_aspect(struct Image *ima, float *aspx, float *aspy); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index e9884c5d8ce..4abba91b716 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -182,6 +182,12 @@ int EDBM_init_backbuf_circle(struct ViewContext *vc, short xs, short ys, short void EDBM_deselect_by_material(struct BMEditMesh *em, const short index, const short select); +struct UvElementMap *EDBM_make_uv_element_map(struct BMEditMesh *em, int selected, int doIslands); +void EDBM_free_uv_element_map(struct UvElementMap *vmap); + +void EDBM_add_data_layer(struct BMEditMesh *em, struct CustomData *data, int type, const char *name); +void EDBM_free_data_layer(struct BMEditMesh *em, struct CustomData *data, int type); + void EDBM_select_swap(struct BMEditMesh *em); /* exported for UV */ int EDBM_texFaceCheck(struct BMEditMesh *em); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 2f193292eec..c19872213ca 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -84,7 +84,7 @@ void ED_uvedit_live_unwrap_end(short cancel); void ED_unwrap_lscm(struct Scene *scene, struct Object *obedit, const short sel); /* uvedit_draw.c */ -void draw_uvedit_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene, struct Object *obedit); +void draw_uvedit_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene, struct Object *obedit, struct Object *obact); /* uvedit_buttons.c */ void ED_uvedit_buttons_register(struct ARegionType *art); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 362fc9fcef9..29585d1c5a3 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -283,10 +283,10 @@ void ED_view3d_draw_offscreen(struct Scene *scene, struct View3D *v3d, struct AR int winx, int winy, float viewmat[][4], float winmat[][4]); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, char err_out[256]); -struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, char err_out[256]); +struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, char err_out[256]); -Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); +struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, short do_clip); int ED_view3d_lock(struct RegionView3D *rv3d); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 5d95992f7d4..852b030c0d2 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -255,7 +255,15 @@ enum { TH_PATH_BEFORE, TH_PATH_AFTER, TH_CAMERA_PATH, - TH_LOCK_MARKER + TH_LOCK_MARKER, + + TH_STITCH_PREVIEW_FACE, + TH_STITCH_PREVIEW_EDGE, + TH_STITCH_PREVIEW_VERT, + TH_STITCH_PREVIEW_STITCHABLE, + TH_STITCH_PREVIEW_UNSTITCHABLE, + TH_STITCH_PREVIEW_ACTIVE + }; /* XXX WARNING: previous is saved in file, so do not change order! */ diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index c3fe50edcd3..ad20ff0b6dd 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -410,6 +410,28 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp= ts->preview_back; break; + case TH_STITCH_PREVIEW_FACE: + cp = ts->preview_stitch_face; + break; + + case TH_STITCH_PREVIEW_EDGE: + cp = ts->preview_stitch_edge; + break; + + case TH_STITCH_PREVIEW_VERT: + cp = ts->preview_stitch_vert; + break; + + case TH_STITCH_PREVIEW_STITCHABLE: + cp = ts->preview_stitch_stitchable; + break; + + case TH_STITCH_PREVIEW_UNSTITCHABLE: + cp = ts->preview_stitch_unstitchable; + break; + case TH_STITCH_PREVIEW_ACTIVE: + cp = ts->preview_stitch_active; + break; case TH_MARKER_OUTLINE: cp= ts->marker_outline; break; case TH_MARKER: @@ -746,6 +768,11 @@ void ui_theme_init_default(void) SETCOL(btheme->tima.face_select, 255, 133, 0, 60); SETCOL(btheme->tima.editmesh_active, 255, 255, 255, 128); SETCOLF(btheme->tima.preview_back, 0.45, 0.45, 0.45, 1.0); + SETCOLF(btheme->tima.preview_stitch_face, 0.5, 0.5, 0.0, 0.2); + SETCOLF(btheme->tima.preview_stitch_edge, 1.0, 0.0, 1.0, 0.2); + SETCOLF(btheme->tima.preview_stitch_vert, 0.0, 0.0, 1.0, 0.2); + SETCOLF(btheme->tima.preview_stitch_stitchable, 0.0, 1.0, 0.0, 1.0); + SETCOLF(btheme->tima.preview_stitch_unstitchable, 1.0, 0.0, 0.0, 1.0); /* space text */ btheme->text= btheme->tv3d; @@ -1679,6 +1706,19 @@ void init_userdef_do_versions(void) } } + if (bmain->versionfile < 262){ + bTheme *btheme; + for(btheme= U.themes.first; btheme; btheme= btheme->next) { + SETCOLF(btheme->tima.preview_stitch_face, 0.071, 0.259, 0.694, 0.150); + SETCOLF(btheme->tima.preview_stitch_edge, 1.0, 0.522, 0.0, 0.7); + SETCOLF(btheme->tima.preview_stitch_vert, 1.0, 0.522, 0.0, 0.5); + SETCOLF(btheme->tima.preview_stitch_stitchable, 0.0, 1.0, 0.0, 1.0); + SETCOLF(btheme->tima.preview_stitch_unstitchable, 1.0, 0.0, 0.0, 1.0); + SETCOLF(btheme->tima.preview_stitch_active, 0.886, 0.824, 0.765, 0.140); + } + U.use_16bit_textures = 0; + } + /* GL Texture Garbage Collection (variable abused above!) */ if (U.textimeout == 0) { U.texcollectrate = 60; diff --git a/source/blender/editors/mesh/bmeshutils.c b/source/blender/editors/mesh/bmeshutils.c index 6defe21e6c2..1d3851b2650 100644 --- a/source/blender/editors/mesh/bmeshutils.c +++ b/source/blender/editors/mesh/bmeshutils.c @@ -752,6 +752,230 @@ UvMapVert *EDBM_get_uv_map_vert(UvVertMap *vmap, unsigned int v) return vmap->vert[v]; } +/* from editmesh_lib.c in trunk */ +#if 0 /* BMESH_TODO */ + +/* A specialized vert map used by stitch operator */ +UvElementMap *EDBM_make_uv_element_map(EditMesh *em, int selected, int do_islands) +{ + EditVert *ev; + EditFace *efa; + + /* vars from original func */ + UvElementMap *vmap; + UvElement *buf; + UvElement *islandbuf; + MTFace *tf; + unsigned int a; + int i,j, totuv, nverts, nislands = 0, islandbufsize = 0; + unsigned int *map; + /* for uv island creation */ + EditFace **stack; + int stacksize = 0; + + /* we need the vert */ + for(ev = em->verts.first, i = 0; ev; ev = ev->next, i++) + ev->tmp.l = i; + + totuv = 0; + + for(efa = em->faces.first; efa; efa = efa->next) + if(!selected || ((!efa->h) && (efa->f & SELECT))) + totuv += (efa->v4)? 4: 3; + + if(totuv == 0) + return NULL; + + vmap = (UvElementMap *)MEM_callocN(sizeof(*vmap), "UvVertElementMap"); + if(!vmap) + return NULL; + + vmap->vert = (UvElement**)MEM_callocN(sizeof(*vmap->vert)*em->totvert, "UvElementVerts"); + buf = vmap->buf = (UvElement*)MEM_callocN(sizeof(*vmap->buf)*totuv, "UvElement"); + + if(!vmap->vert || !vmap->buf) { + EDBM_free_uv_element_map(vmap); + return NULL; + } + + vmap->totalUVs = totuv; + + for(efa = em->faces.first; efa; a++, efa = efa->next) { + if(!selected || ((!efa->h) && (efa->f & SELECT))) { + nverts = (efa->v4)? 4: 3; + + for(i = 0; i<nverts; i++) { + buf->tfindex = i; + buf->face = efa; + buf->separate = 0; + buf->island = INVALID_ISLAND; + + buf->next = vmap->vert[(*(&efa->v1 + i))->tmp.l]; + vmap->vert[(*(&efa->v1 + i))->tmp.l] = buf; + + buf++; + } + } + + efa->tmp.l = INVALID_ISLAND; + } + + /* sort individual uvs for each vert */ + for(a = 0, ev = em->verts.first; ev; a++, ev = ev->next) { + UvElement *newvlist = NULL, *vlist = vmap->vert[a]; + UvElement *iterv, *v, *lastv, *next; + float *uv, *uv2; + + while(vlist) { + v= vlist; + vlist= vlist->next; + v->next= newvlist; + newvlist= v; + + efa = v->face; + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv = tf->uv[v->tfindex]; + + lastv= NULL; + iterv= vlist; + + while(iterv) { + next= iterv->next; + efa = iterv->face; + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv2 = tf->uv[iterv->tfindex]; + + if(fabsf(uv[0]-uv2[0]) < STD_UV_CONNECT_LIMIT && fabsf(uv[1]-uv2[1]) < STD_UV_CONNECT_LIMIT) { + if(lastv) lastv->next = next; + else vlist = next; + iterv->next = newvlist; + newvlist = iterv; + } + else + lastv = iterv; + + iterv = next; + } + + newvlist->separate = 1; + } + + vmap->vert[a] = newvlist; + } + + if(do_islands) { + /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. Now we should sort uv's in islands. */ + + /* map holds the map from current vmap->buf to the new, sorted map*/ + map = MEM_mallocN(sizeof(*map)*totuv, "uvelement_remap"); + stack = MEM_mallocN(sizeof(*stack)*em->totface, "uv_island_face_stack"); + islandbuf = MEM_callocN(sizeof(*islandbuf)*totuv, "uvelement_island_buffer"); + + for(i = 0; i < totuv; i++) { + if(vmap->buf[i].island == INVALID_ISLAND) { + vmap->buf[i].island = nislands; + stack[0] = vmap->buf[i].face; + stack[0]->tmp.l = nislands; + stacksize=1; + + while(stacksize > 0) { + efa = stack[--stacksize]; + nverts = efa->v4? 4 : 3; + + for(j = 0; j < nverts; j++) { + UvElement *element, *initelement = vmap->vert[(*(&efa->v1 + j))->tmp.l]; + + for(element = initelement; element; element = element->next) { + if(element->separate) + initelement = element; + + if(element->face == efa) { + /* found the uv corresponding to our face and vertex. Now fill it to the buffer */ + element->island = nislands; + map[element - vmap->buf] = islandbufsize; + islandbuf[islandbufsize].tfindex = element->tfindex; + islandbuf[islandbufsize].face = element->face; + islandbuf[islandbufsize].separate = element->separate; + islandbuf[islandbufsize].island = nislands; + islandbufsize++; + + for(element = initelement; element; element = element->next) { + if(element->separate && element != initelement) + break; + + if(element->face->tmp.l == INVALID_ISLAND) { + stack[stacksize++] = element->face; + element->face->tmp.l = nislands; + } + } + break; + } + } + } + } + + nislands++; + } + } + + /* remap */ + for(i = 0; i < em->totvert; i++) { + /* important since we may do selection only. Some of these may be NULL */ + if(vmap->vert[i]) + vmap->vert[i] = &islandbuf[map[vmap->vert[i] - vmap->buf]]; + } + + vmap->islandIndices = MEM_callocN(sizeof(*vmap->islandIndices)*nislands,"UvVertMap2_island_indices"); + if(!vmap->islandIndices) { + MEM_freeN(islandbuf); + MEM_freeN(stack); + MEM_freeN(map); + EDBM_free_uv_element_map(vmap); + } + + j = 0; + for(i = 0; i < totuv; i++) { + UvElement *element = vmap->buf[i].next; + if(element == NULL) + islandbuf[map[i]].next = NULL; + else + islandbuf[map[i]].next = &islandbuf[map[element - vmap->buf]]; + + if(islandbuf[i].island != j) { + j++; + vmap->islandIndices[j] = i; + } + } + + MEM_freeN(vmap->buf); + + vmap->buf = islandbuf; + vmap->totalIslands = nislands; + MEM_freeN(stack); + MEM_freeN(map); + } + + return vmap; +} + +#else + +UvElementMap *EDBM_make_uv_element_map(BMEditMesh *em, int selected, int do_islands) +{ + (void)em; + (void)selected; + (void)do_islands; + + return NULL; +} + +#endif /* BMESH_TODO */ + +UvMapVert *EM_get_uv_map_vert(UvVertMap *vmap, unsigned int v) +{ + return vmap->vert[v]; +} + void EDBM_free_uv_vert_map(UvVertMap *vmap) { if (vmap) { @@ -761,6 +985,15 @@ void EDBM_free_uv_vert_map(UvVertMap *vmap) } } +void EDBM_free_uv_element_map(UvElementMap *vmap) +{ + if (vmap) { + if (vmap->vert) MEM_freeN(vmap->vert); + if (vmap->buf) MEM_freeN(vmap->buf); + if (vmap->islandIndices) MEM_freeN(vmap->islandIndices); + MEM_freeN(vmap); + } +} /* last_sel, use em->act_face otherwise get the last selected face in the editselections * at the moment, last_sel is mainly useful for gaking sure the space image dosnt flicker */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 5f313301e2e..a7ac91d13df 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -89,7 +89,7 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_util.h" - +#include "ED_image.h" #include "RNA_access.h" #include "RNA_define.h" @@ -514,11 +514,15 @@ void ED_object_enter_editmode(bContext *C, int flag) static int editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { + ToolSettings *toolsettings = CTX_data_tool_settings(C); + if(!CTX_data_edit_object(C)) ED_object_enter_editmode(C, EM_WAITCURSOR); else ED_object_exit_editmode(C, EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR); /* had EM_DO_UNDO but op flag calls undo too [#24685] */ + ED_space_image_uv_sculpt_update(CTX_wm_manager(C), toolsettings); + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 30f49264e90..29e97c77bec 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenkernel ../../blenlib ../../blenloader + ../../bmesh ../../gpu ../../imbuf ../../makesdna ../../makesrna + ../uvedit ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc @@ -45,6 +47,7 @@ set(SRC paint_vertex.c sculpt.c sculpt_undo.c + sculpt_uv.c paint_intern.h sculpt_intern.h diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript index 8f1f47b5004..8669ea6c695 100644 --- a/source/blender/editors/sculpt_paint/SConscript +++ b/source/blender/editors/sculpt_paint/SConscript @@ -8,7 +8,7 @@ defs = [] incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' incs += ' ../../render/extern/include' -incs += ' ../../gpu ../../makesrna ../../blenloader ../../bmesh' +incs += ' ../../gpu ../../makesrna ../../blenloader ../../bmesh ../uvedit' if env['OURPLATFORM'] == 'linux': cflags='-pthread' diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 5f4777331c3..1e989cce14a 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -50,6 +50,7 @@ #include "BLI_memarena.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_editVert.h" #include "PIL_time.h" @@ -80,6 +81,10 @@ #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_global.h" +#include "BKE_deform.h" + +#include "BKE_tessmesh.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -91,6 +96,7 @@ #include "ED_sculpt.h" #include "ED_uvedit.h" #include "ED_view3d.h" +#include "ED_mesh.h" #include "WM_api.h" #include "WM_types.h" @@ -100,6 +106,7 @@ #include "RNA_enum_types.h" #include "GPU_draw.h" +#include "GPU_extensions.h" #include "paint_intern.h" @@ -3742,15 +3749,13 @@ static void do_projectpaint_smear(ProjPaintState *ps, ProjPixel *projPixel, floa static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, float alpha, float mask, MemArena *smearArena, LinkNode **smearPixels_f, float co[2]) { - unsigned char rgba_ub[4]; - unsigned char rgba_smear[4]; + float rgba[4]; - if (project_paint_PickColor(ps, co, NULL, rgba_ub, 1)==0) + if (project_paint_PickColor(ps, co, rgba, NULL, 1)==0) return; - IMAPAINT_FLOAT_RGBA_TO_CHAR(rgba_smear, projPixel->pixel.f_pt); /* (ProjPixelClone *)projPixel)->clonepx.uint = IMB_blend_color(*((unsigned int *)rgba_smear), *((unsigned int *)rgba_ub), (int)(alpha*mask*255), ps->blend); */ - blend_color_mix(((ProjPixelClone *)projPixel)->clonepx.ch, rgba_smear, (rgba_ub), (int)(alpha*mask*255)); + blend_color_mix_float(((ProjPixelClone *)projPixel)->clonepx.f, projPixel->pixel.f_pt, rgba, alpha*mask); BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); } @@ -3782,8 +3787,8 @@ static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, flo { if (ps->is_texbrush) { /* rgba already holds a texture result here from higher level function */ - float rgba_br[3]; if(use_color_correction){ + float rgba_br[3]; srgb_to_linearrgb_v3_v3(rgba_br, ps->brush->rgb); mul_v3_v3(rgba, rgba_br); } @@ -3999,7 +4004,7 @@ static void *do_projectpaint_thread(void *ph_v) for (node= smearPixels_f; node; node= node->next) { projPixel = node->link; - IMAPAINT_CHAR_RGBA_TO_FLOAT(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.ch); + copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); } BLI_memarena_free(smearArena); @@ -4167,7 +4172,8 @@ static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, s if(texpaint || (sima && sima->lock)) { int w = imapaintpartial.x2 - imapaintpartial.x1; int h = imapaintpartial.y2 - imapaintpartial.y1; - GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h, !texpaint); + /* Testing with partial update in uv editor too */ + GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h, 0);//!texpaint); } } @@ -4606,6 +4612,16 @@ static Brush *image_paint_brush(bContext *C) return paint_brush(&settings->imapaint.paint); } +static Brush *uv_sculpt_brush(bContext *C) +{ + Scene *scene= CTX_data_scene(C); + ToolSettings *settings= scene->toolsettings; + + if(!settings->uvsculpt) + return NULL; + return paint_brush(&settings->uvsculpt->paint); +} + static int image_paint_poll(bContext *C) { Object *obact = CTX_data_active_object(C); @@ -4630,6 +4646,30 @@ static int image_paint_poll(bContext *C) return 0; } +static int uv_sculpt_brush_poll(bContext *C) +{ + BMEditMesh *em; + int ret; + Object *obedit = CTX_data_edit_object(C); + SpaceImage *sima= CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = scene->toolsettings; + + if(!uv_sculpt_brush(C) || !obedit || obedit->type != OB_MESH) + return 0; + + em = ((Mesh *)obedit->data)->edit_btmesh; + ret = EDBM_texFaceCheck(em); + + if(ret && sima) { + ARegion *ar= CTX_wm_region(C); + if((toolsettings->use_uv_sculpt) && ar->regiontype==RGN_TYPE_WINDOW) + return 1; + } + + return 0; +} + static int image_paint_3d_poll(bContext *C) { if(CTX_wm_region_view3d(C)) @@ -5086,7 +5126,7 @@ void PAINT_OT_image_paint(wmOperatorType *ot) RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); } -static int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) +int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) { RegionView3D *rv3d= CTX_wm_region_view3d(C); @@ -5112,16 +5152,27 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata) #define PX_SIZE_FADE_MIN 4.0f Scene *scene= CTX_data_scene(C); - Brush *brush= image_paint_brush(C); + //Brush *brush= image_paint_brush(C); Paint *paint= paint_get_active(scene); + Brush *brush= paint_brush(paint); if(paint && brush && paint->flags & PAINT_SHOW_BRUSH) { + ToolSettings *ts; float zoomx, zoomy; const float size= (float)brush_size(scene, brush); const short use_zoom= get_imapaint_zoom(C, &zoomx, &zoomy); - const float pixel_size= MAX2(size * zoomx, size * zoomy); + float pixel_size; float alpha= 0.5f; + ts = scene->toolsettings; + + if(use_zoom && !ts->use_uv_sculpt){ + pixel_size = MAX2(size * zoomx, size * zoomy); + } + else { + pixel_size = size; + } + /* fade out the brush (cheap trick to work around brush interfearing with sampling [#])*/ if(pixel_size < PX_SIZE_FADE_MIN) { return; @@ -5134,7 +5185,8 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata) glTranslatef((float)x, (float)y, 0.0f); - if(use_zoom) + /* No need to scale for uv sculpting, on the contrary it might be useful to keep unscaled */ + if(use_zoom && !ts->use_uv_sculpt) glScalef(zoomx, zoomy, 1.0f); glColor4f(brush->add_col[0], brush->add_col[1], brush->add_col[2], alpha); @@ -5152,14 +5204,16 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata) static void toggle_paint_cursor(bContext *C, int enable) { - ToolSettings *settings= CTX_data_scene(C)->toolsettings; + wmWindowManager *wm= CTX_wm_manager(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings= scene->toolsettings; if(settings->imapaint.paintcursor && !enable) { - WM_paint_cursor_end(CTX_wm_manager(C), settings->imapaint.paintcursor); + WM_paint_cursor_end(wm, settings->imapaint.paintcursor); settings->imapaint.paintcursor = NULL; } else if(enable) - settings->imapaint.paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, brush_drawcursor, NULL); + settings->imapaint.paintcursor= WM_paint_cursor_activate(wm, image_paint_poll, brush_drawcursor, NULL); } /* enable the paint cursor if it isn't already. @@ -5178,6 +5232,27 @@ void ED_space_image_paint_update(wmWindowManager *wm, ToolSettings *settings) } } + +void ED_space_image_uv_sculpt_update(wmWindowManager *wm, ToolSettings *settings) +{ + if(settings->use_uv_sculpt) { + if(!settings->uvsculpt) { + settings->uvsculpt = MEM_callocN(sizeof(*settings->uvsculpt), "UV Smooth paint"); + settings->uv_sculpt_tool = UV_SCULPT_TOOL_GRAB; + settings->uv_sculpt_settings = UV_SCULPT_LOCK_BORDERS | UV_SCULPT_ALL_ISLANDS; + settings->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN; + } + + paint_init(&settings->uvsculpt->paint, PAINT_CURSOR_SCULPT); + + WM_paint_cursor_activate(wm, uv_sculpt_brush_poll, + brush_drawcursor, NULL); + } + else { + if(settings->uvsculpt) + settings->uvsculpt->paint.flags &= ~PAINT_SHOW_BRUSH; + } +} /************************ grab clone operator ************************/ typedef struct GrabClone { @@ -5499,6 +5574,11 @@ int image_texture_paint_poll(bContext *C) return (texture_paint_poll(C) || image_paint_poll(C)); } +int uv_sculpt_poll(bContext *C) +{ + return uv_sculpt_brush_poll(C); +} + int facemask_paint_poll(bContext *C) { return paint_facesel_test(CTX_data_active_object(C)); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 2fc7d569d63..4ca05e1fbd7 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -103,6 +103,10 @@ void PAINT_OT_texture_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_project_image(struct wmOperatorType *ot); void PAINT_OT_image_from_view(struct wmOperatorType *ot); +/* uv sculpting */ +int uv_sculpt_poll(struct bContext *C); + +void SCULPT_OT_uv_sculpt_stroke(struct wmOperatorType *ot); /* paint_utils.c */ diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index d4578e17b3d..0ff0f278f17 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -340,6 +340,39 @@ static void BRUSH_OT_image_tool_set(wmOperatorType *ot) } +static int brush_uv_sculpt_tool_set_exec(bContext *C, wmOperator *op) +{ + Brush *brush; + Scene *scene= CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + ts->uv_sculpt_tool = RNA_enum_get(op->ptr, "tool"); + brush = ts->uvsculpt->paint.brush; + /* To update toolshelf */ + WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush); + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_uv_sculpt_tool_set(wmOperatorType *ot) +{ + /* from rna_scene.c */ + extern EnumPropertyItem uv_sculpt_tool_items[]; + /* identifiers */ + ot->name = "UV Sculpt Tool Set"; + ot->description = "Set the uv sculpt tool"; + ot->idname = "BRUSH_OT_uv_sculpt_tool_set"; + + /* api callbacks */ + ot->exec = brush_uv_sculpt_tool_set_exec; + ot->poll = uv_sculpt_poll; + + /* flags */ + ot->flag = 0; + + /* props */ + ot->prop = RNA_def_enum(ot->srna, "tool", uv_sculpt_tool_items, 0, "Tool", ""); +} + /**************************** registration **********************************/ void ED_operatortypes_paint(void) @@ -355,6 +388,7 @@ void ED_operatortypes_paint(void) WM_operatortype_append(BRUSH_OT_vertex_tool_set); WM_operatortype_append(BRUSH_OT_weight_tool_set); WM_operatortype_append(BRUSH_OT_image_tool_set); + WM_operatortype_append(BRUSH_OT_uv_sculpt_tool_set); /* image */ WM_operatortype_append(PAINT_OT_texture_paint_toggle); @@ -373,6 +407,9 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_weight_sample); WM_operatortype_append(PAINT_OT_weight_sample_group); + /* uv */ + WM_operatortype_append(SCULPT_OT_uv_sculpt_stroke); + /* vertex selection */ WM_operatortype_append(PAINT_OT_vert_select_all); WM_operatortype_append(PAINT_OT_vert_select_inverse); @@ -619,4 +656,22 @@ void ED_keymap_paint(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "PAINT_OT_face_select_linked", LKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "PAINT_OT_face_select_linked_pick", LKEY, KM_PRESS, 0, 0); + + keymap= WM_keymap_find(keyconf, "UV Sculpt", 0, 0); + keymap->poll= uv_sculpt_poll; + + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt"); + + WM_keymap_add_item(keymap, "SCULPT_OT_uv_sculpt_stroke", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_uv_sculpt_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "invert", 1); + RNA_boolean_set(WM_keymap_add_item(keymap, "SCULPT_OT_uv_sculpt_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "temp_relax", 1); + + ed_keymap_paint_brush_size(keymap, "tool_settings.uv_sculpt.brush.size"); + ed_keymap_paint_brush_radial_control(keymap, "uv_sculpt", 0); + + RNA_enum_set(WM_keymap_add_item(keymap, "BRUSH_OT_uv_sculpt_tool_set", SKEY, KM_PRESS, 0, 0)->ptr, "tool", UV_SCULPT_TOOL_RELAX); + RNA_enum_set(WM_keymap_add_item(keymap, "BRUSH_OT_uv_sculpt_tool_set", PKEY, KM_PRESS, 0, 0)->ptr, "tool", UV_SCULPT_TOOL_PINCH); + RNA_enum_set(WM_keymap_add_item(keymap, "BRUSH_OT_uv_sculpt_tool_set", GKEY, KM_PRESS, 0, 0)->ptr, "tool", UV_SCULPT_TOOL_GRAB); + } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c new file mode 100644 index 00000000000..21bbb014eb0 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -0,0 +1,787 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) Blender Foundation, 2002-2009 + * All rights reserved. + * + * Contributor(s): Antony Riakiotakis + * + * ***** END GPL LICENSE BLOCK ***** + * + * UV Sculpt tools + * + */ + +/** \file blender/editors/sculpt_paint/sculpt_uv.c + * \ingroup edsculpt + */ + + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_editVert.h" +#include "BLI_math.h" +#include "BLI_ghash.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_brush_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_brush.h" +#include "BKE_paint.h" +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_depsgraph.h" +#include "BKE_mesh.h" +#include "BKE_customdata.h" +#include "BKE_tessmesh.h" + +#include "ED_screen.h" +#include "ED_image.h" +#include "ED_mesh.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "paint_intern.h" +#include "uvedit_intern.h" + +#include "UI_view2d.h" + +#define MARK_BOUNDARY 1 + +typedef struct UvAdjacencyElement { + /* pointer to original uvelement */ + UvElement *element; + /* uv pointer for convenience. Caution, this points to the original UVs! */ + float *uv; + /* general use flag (Used to check if Element is boundary here) */ + char flag; +} UvAdjacencyElement; + +typedef struct UvEdge { + unsigned int uv1; + unsigned int uv2; + /* general use flag (Used to check if edge is boundary here, and propagates to adjacency elements) */ + char flag; +}UvEdge; + +typedef struct UVInitialStrokeElement{ + /* index to unique uv */ + int uv; + + /* strength of brush on initial position */ + float strength; + + /* initial uv position */ + float initial_uv[2]; +}UVInitialStrokeElement; + +typedef struct UVInitialStroke{ + /* Initial Selection,for grab brushes for instance */ + UVInitialStrokeElement *initialSelection; + + /* total initially selected UVs*/ + int totalInitialSelected; + + /* initial mouse coordinates */ + float init_coord[2]; +}UVInitialStroke; + + +/* custom data for uv smoothing brush */ +typedef struct UvSculptData{ + /* Contains the first of each set of coincident uvs. + * These will be used to perform smoothing on and propagate the changes + * to their coincident uvs */ + UvAdjacencyElement *uv; + + /* ...Is what it says */ + int totalUniqueUvs; + + /* Edges used for adjacency info, used with laplacian smoothing */ + UvEdge *uvedges; + + /* Need I say more? */ + int totalUvEdges; + + /* data for initial stroke, used by tools like grab */ + UVInitialStroke *initial_stroke; + + /* Timer to be used for airbrush-type brush */ + wmTimer *timer; + + /* To determine quickly adjacent uvs */ + UvElementMap *elementMap; + + /* uvsmooth Paint for fast reference */ + Paint *uvsculpt; +}UvSculptData; + +/*********** Improved Laplacian Relaxation Operator ************************/ +/* Original code by Raul Fernandez Hernandez "farsthary" * + * adapted to uv smoothing by Antony Riakiatakis * + ***************************************************************************/ + +typedef struct Temp_UvData{ + float sum_co[2], p[2], b[2], sum_b[2]; + int ncounter; +}Temp_UVData; + + + +void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, float mouse_coord[2], float alpha, float radius, float aspectRatio){ + Temp_UVData *tmp_uvdata; + float diff[2]; + int i; + float radius_root = sqrt(radius); + Brush *brush = paint_brush(sculptdata->uvsculpt); + + tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); + + /* counting neighbors */ + for (i = 0; i < sculptdata->totalUvEdges; i++){ + UvEdge *tmpedge = sculptdata->uvedges+i; + tmp_uvdata[tmpedge->uv1].ncounter++; + tmp_uvdata[tmpedge->uv2].ncounter++; + + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + copy_v2_v2(diff,tmp_uvdata[i].sum_co); + mul_v2_fl(diff,1.f/tmp_uvdata[i].ncounter); + copy_v2_v2(tmp_uvdata[i].p,diff); + + tmp_uvdata[i].b[0] = diff[0] - sculptdata->uv[i].uv[0]; + tmp_uvdata[i].b[1] = diff[1] - sculptdata->uv[i].uv[1]; + } + + for (i = 0; i < sculptdata->totalUvEdges; i++){ + UvEdge *tmpedge = sculptdata->uvedges+i; + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_b, tmp_uvdata[tmpedge->uv2].b); + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_b, tmp_uvdata[tmpedge->uv1].b); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + float dist; + /* This is supposed to happen only if "Pin Edges" is on, since we have initialization on stroke start + * If ever uv brushes get their own mode we should check for toolsettings option too */ + if((sculptdata->uv[i].flag & MARK_BOUNDARY)){ + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + UvElement *element; + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + + sculptdata->uv[i].uv[0] = (1.0-strength)*sculptdata->uv[i].uv[0] + strength*(tmp_uvdata[i].p[0] - 0.5f*(tmp_uvdata[i].b[0] + tmp_uvdata[i].sum_b[0]/tmp_uvdata[i].ncounter)); + sculptdata->uv[i].uv[1] = (1.0-strength)*sculptdata->uv[i].uv[1] + strength*(tmp_uvdata[i].p[1] - 0.5f*(tmp_uvdata[i].b[1] + tmp_uvdata[i].sum_b[1]/tmp_uvdata[i].ncounter)); + + for(element = sculptdata->uv[i].element; element; element = element->next){ +#if 0 /* BMESH_TODO */ + MTFace *mt; + if(element->separate && element != sculptdata->uv[i].element) + break; + mt = CustomData_em_get(&em->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[i].uv); +#else + (void)em; +#endif /* BMESH_TODO */ + } + } + } + + MEM_freeN(tmp_uvdata); + + return; +} + +static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, float mouse_coord[2], float alpha, float radius, float aspectRatio) +{ + Temp_UVData *tmp_uvdata; + float diff[2]; + int i; + float radius_root = sqrt(radius); + Brush *brush = paint_brush(sculptdata->uvsculpt); + + tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); + + /* counting neighbors */ + for (i = 0; i < sculptdata->totalUvEdges; i++){ + UvEdge *tmpedge = sculptdata->uvedges+i; + tmp_uvdata[tmpedge->uv1].ncounter++; + tmp_uvdata[tmpedge->uv2].ncounter++; + + add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv); + add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv); + } + + /* Original Lacplacian algorithm included removal of normal component of translation. here it is not + * needed since we translate along the UV plane always.*/ + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co); + mul_v2_fl(tmp_uvdata[i].p, 1.f/tmp_uvdata[i].ncounter); + } + + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + float dist; + /* This is supposed to happen only if "Pin Edges" is on, since we have initialization on stroke start + * If ever uv brushes get their own mode we should check for toolsettings option too */ + if((sculptdata->uv[i].flag & MARK_BOUNDARY)){ + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + UvElement *element; + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + + sculptdata->uv[i].uv[0] = (1.0-strength)*sculptdata->uv[i].uv[0] + strength*tmp_uvdata[i].p[0]; + sculptdata->uv[i].uv[1] = (1.0-strength)*sculptdata->uv[i].uv[1] + strength*tmp_uvdata[i].p[1]; + + for(element = sculptdata->uv[i].element; element; element = element->next){ +#if 0 /* BMESH_TODO */ + MTFace *mt; + if(element->separate && element != sculptdata->uv[i].element) + break; + mt = CustomData_em_get(&em->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[i].uv); +#else + (void)em; +#endif /* BMESH_TODO */ + } + } + } + + MEM_freeN(tmp_uvdata); + + return; +} + + +static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, wmEvent *event, Object *obedit) +{ + float co[2], radius, radius_root; + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh; + unsigned int tool; + UvSculptData *sculptdata = (UvSculptData *)op->customdata; + SpaceImage *sima; + int invert; + int width, height; + float aspectRatio; + float alpha, zoomx, zoomy; + Brush *brush = paint_brush(sculptdata->uvsculpt); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + tool = RNA_boolean_get(op->ptr, "temp_relax")? UV_SCULPT_TOOL_RELAX : toolsettings->uv_sculpt_tool; + + invert = RNA_boolean_get(op->ptr, "invert")? -1 : 1; + alpha = brush_alpha(scene, brush); + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + sima = CTX_wm_space_image(C); + ED_space_image_size(sima, &width, &height); + ED_space_image_zoom(sima, ar, &zoomx, &zoomy); + + radius = brush_size(scene, brush)/(width*zoomx); + aspectRatio = width/(float)height; + + /* We will compare squares to save some computation */ + radius = radius*radius; + radius_root = sqrt(radius); + + /* + * Pinch Tool + */ + if(tool == UV_SCULPT_TOOL_PINCH){ + int i; + alpha *= invert; + for (i = 0; i < sculptdata->totalUniqueUvs; i++){ + float dist, diff[2]; + /* This is supposed to happen only if "Lock Borders" is on, since we have initialization on stroke start + * If ever uv brushes get their own mode we should check for toolsettings option too */ + if(sculptdata->uv[i].flag & MARK_BOUNDARY){ + continue; + } + + sub_v2_v2v2(diff, sculptdata->uv[i].uv, co); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + UvElement *element; + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + normalize_v2(diff); + + sculptdata->uv[i].uv[0] -= strength*diff[0]*0.001; + sculptdata->uv[i].uv[1] -= strength*diff[1]*0.001; + + for(element = sculptdata->uv[i].element; element; element = element->next){ +#if 0 /* BMESH_TODO*/ + MTFace *mt; + if(element->separate && element != sculptdata->uv[i].element) + break; + mt = CustomData_em_get(&bm->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[i].uv); +#endif + } + } + } + } + + /* + * Smooth Tool + */ + else if(tool == UV_SCULPT_TOOL_RELAX){ + unsigned int method = toolsettings->uv_relax_method; + if(method == UV_SCULPT_TOOL_RELAX_HC){ + HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); + }else{ + laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio); + } + } + + /* + * Grab Tool + */ + else if(tool == UV_SCULPT_TOOL_GRAB){ + int i; + float diff[2]; + sub_v2_v2v2(diff, co, sculptdata->initial_stroke->init_coord); + + for(i = 0; i < sculptdata->initial_stroke->totalInitialSelected; i++ ){ + UvElement *element; + int uvindex = sculptdata->initial_stroke->initialSelection[i].uv; + float strength = sculptdata->initial_stroke->initialSelection[i].strength; + sculptdata->uv[uvindex].uv[0] = sculptdata->initial_stroke->initialSelection[i].initial_uv[0] + strength*diff[0]; + sculptdata->uv[uvindex].uv[1] = sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength*diff[1]; + + for(element = sculptdata->uv[uvindex].element; element; element = element->next){ +#if 0 /* BMESH_TODO */ + MTFace *mt; + if(element->separate && element != sculptdata->uv[uvindex].element) + break; + mt = CustomData_em_get(&em->fdata, element->face->data, CD_MTFACE); + copy_v2_v2(mt->uv[element->tfindex], sculptdata->uv[uvindex].uv); +#endif /* BMESH_TODO */ + } + } + } +} + + +static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) +{ + UvSculptData *data = op->customdata; + if(data->timer){ + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); + } + if(data->elementMap) + { + EDBM_free_uv_element_map(data->elementMap); + } + if(data->uv){ + MEM_freeN(data->uv); + } + if(data->uvedges){ + MEM_freeN(data->uvedges); + } + if(data->initial_stroke){ + if(data->initial_stroke->initialSelection){ + MEM_freeN(data->initial_stroke->initialSelection); + } + MEM_freeN(data->initial_stroke); + } + + MEM_freeN(data); + op->customdata = NULL; +} + +static int get_uv_element_offset_from_face(UvElementMap *map, BMFace *efa, int index, int island_index, int doIslands){ + UvElement *element = ED_get_uv_element(map, efa, index); + if(!element || (doIslands && element->island != island_index)){ + return -1; + } + return element - map->buf; +} + + +static unsigned int uv_edge_hash(const void *key){ + UvEdge *edge = (UvEdge *)key; + return + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv2)) + + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv1)); +} + +static int uv_edge_compare(const void *a, const void *b){ + UvEdge *edge1 = (UvEdge *)a; + UvEdge *edge2 = (UvEdge *)b; + + if((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)){ + return 0; + } + return 1; +} + + +static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, wmEvent *event) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + ToolSettings *ts = scene->toolsettings; + UvSculptData *data = MEM_callocN(sizeof(*data), "UV Smooth Brush Data"); + BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh; + BMesh *bm = em->bm; + + op->customdata = data; + + if(data){ + int counter = 0, i; + ARegion *ar= CTX_wm_region(C); + float co[2]; + EditFace *efa; + UvEdge *edges; + GHash *edgeHash; + GHashIterator* ghi; + MTFace *mt; + int do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS); + int island_index = 0; + /* Holds, for each UvElement in elementMap, a pointer to its unique uv.*/ + int *uniqueUv; + + data->uvsculpt = &ts->uvsculpt->paint; + + if(do_island_optimization){ + /* We will need island information */ + if(ts->uv_flag & UV_SYNC_SELECTION){ + data->elementMap = EDBM_make_uv_element_map(em, 0, 1); + }else{ + data->elementMap = EDBM_make_uv_element_map(em, 1, 1); + } + }else { + if(ts->uv_flag & UV_SYNC_SELECTION){ + data->elementMap = EDBM_make_uv_element_map(em, 0, 0); + }else{ + data->elementMap = EDBM_make_uv_element_map(em, 1, 0); + } + } + + if(!data->elementMap){ + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + /* Mouse coordinates, useful for some functions like grab and sculpt all islands */ + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + /* we need to find the active island here */ + if(do_island_optimization){ + UvElement *element; + NearestHit hit; + Image *ima= CTX_data_edit_image(C); + uv_find_nearest_vert(scene, ima, em, co, NULL, &hit); + + element = ED_get_uv_element(data->elementMap, hit.efa, hit.lindex); + island_index = element->island; + } + + + /* Count 'unique' uvs */ + for(i = 0; i < data->elementMap->totalUVs; i++){ + if(data->elementMap->buf[i].separate + && (!do_island_optimization || data->elementMap->buf[i].island == island_index)){ + counter++; + } + } + + /* Allocate the unique uv buffers */ + data->uv = MEM_mallocN(sizeof(*data->uv)*counter, "uv_brush_unique_uvs"); + uniqueUv = MEM_mallocN(sizeof(*uniqueUv)*data->elementMap->totalUVs, "uv_brush_unique_uv_map"); + edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); + /* we have at most totalUVs edges */ + edges = MEM_mallocN(sizeof(*edges)*data->elementMap->totalUVs, "uv_brush_all_edges"); + if(!data->uv || !uniqueUv || !edgeHash || !edges){ + if(edges){ + MEM_freeN(edges); + } + if(uniqueUv){ + MEM_freeN(uniqueUv); + } + if(edgeHash){ + MEM_freeN(edgeHash); + } + uv_sculpt_stroke_exit(C, op); + return NULL; + } + + data->totalUniqueUvs = counter; + /* So that we can use this as index for the UvElements */ + counter = -1; + /* initialize the unique UVs */ + for(i = 0; i < bm->totvert; i++){ + UvElement *element = data->elementMap->vert[i]; + for(; element; element = element->next){ + if(element->separate){ + if(do_island_optimization && (element->island != island_index)){ + /* skip this uv if not on the active island */ + for(; element->next && !(element->next->separate); element = element->next) + ; + continue; + } +#if 0 /* BMESH_TODO */ + efa = element->face; + mt = CustomData_em_get(&bm->fdata, efa->data, CD_MTFACE); + + counter++; + data->uv[counter].element = element; + data->uv[counter].flag = 0; + data->uv[counter].uv = mt->uv[element->tfindex]; +#else + (void)efa; + (void)mt; +#endif /* BMESH_TODO */ + } + /* pointer arithmetic to the rescue, as always :)*/ + uniqueUv[element - data->elementMap->buf] = counter; + } + } + +#if 0 /* BMESH_TODO */ + /* Now, on to generate our uv connectivity data */ + for(efa = em->faces.first, counter = 0; efa; efa = efa->next){ + int nverts = efa->v4 ? 4 : 3; + for(i = 0; i < nverts; i++){ + int offset1, itmp1 = get_uv_element_offset_from_face(data->elementMap, efa, i, island_index, do_island_optimization); + int offset2, itmp2 = get_uv_element_offset_from_face(data->elementMap, efa, (i+1)%nverts, island_index, do_island_optimization); + + /* Skip edge if not found(unlikely) or not on valid island */ + if(itmp1 == -1 || itmp2 == -1) + continue; + + offset1 = uniqueUv[itmp1]; + offset2 = uniqueUv[itmp2]; + + edges[counter].flag = 0; + /* using an order policy, sort uvs according to address space. This avoids + * Having two different UvEdges with the same uvs on different positions */ + if(offset1 < offset2){ + edges[counter].uv1 = offset1; + edges[counter].uv2 = offset2; + } + else{ + edges[counter].uv1 = offset2; + edges[counter].uv2 = offset1; + } + /* Hack! Set the value of the key to its flag. Now we can set the flag when an edge exists twice :) */ + if(BLI_ghash_haskey(edgeHash, &edges[counter])){ + char *flag = BLI_ghash_lookup(edgeHash, &edges[counter]); + *flag = 1; + } + else{ + /* Hack mentioned */ + BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag); + } + counter++; + } + } +#endif /* BMESH_TODO */ + + MEM_freeN(uniqueUv); + + /* Allocate connectivity data, we allocate edges once */ + data->uvedges = MEM_mallocN(sizeof(*data->uvedges)*BLI_ghash_size(edgeHash), "uv_brush_edge_connectivity_data"); + if(!data->uvedges){ + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + uv_sculpt_stroke_exit(C, op); + return NULL; + } + ghi = BLI_ghashIterator_new(edgeHash); + if(!ghi){ + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + uv_sculpt_stroke_exit(C, op); + return NULL; + } + /* fill the edges with data */ + for(i = 0; !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){ + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi)); + } + data->totalUvEdges = BLI_ghash_size(edgeHash); + + /* cleanup temporary stuff */ + BLI_ghashIterator_free(ghi); + BLI_ghash_free(edgeHash, NULL, NULL); + MEM_freeN(edges); + + /* transfer boundary edge property to uvs */ + if(ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS){ + for(i = 0; i < data->totalUvEdges; i++){ + if(!data->uvedges[i].flag){ + data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; + data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; + } + } + } + + /* Allocate initial selection for grab tool */ + if(ts->uv_sculpt_tool == UV_SCULPT_TOOL_GRAB){ + float radius, radius_root; + UvSculptData *sculptdata = (UvSculptData *)op->customdata; + SpaceImage *sima; + int width, height; + float aspectRatio; + float alpha, zoomx, zoomy; + Brush *brush = paint_brush(sculptdata->uvsculpt); + + alpha = brush_alpha(scene, brush); + + radius = brush_size(scene, brush); + sima = CTX_wm_space_image(C); + ED_space_image_size(sima, &width, &height); + ED_space_image_zoom(sima, ar, &zoomx, &zoomy); + + aspectRatio = width/(float)height; + radius /= (width*zoomx); + radius = radius*radius; + radius_root = sqrt(radius); + + /* Allocate selection stack */ + data->initial_stroke = MEM_mallocN(sizeof(*data->initial_stroke), "uv_sculpt_initial_stroke"); + if(!data->initial_stroke){ + uv_sculpt_stroke_exit(C, op); + } + data->initial_stroke->initialSelection = MEM_mallocN(sizeof(*data->initial_stroke->initialSelection)*data->totalUniqueUvs, "uv_sculpt_initial_selection"); + if(!data->initial_stroke->initialSelection){ + uv_sculpt_stroke_exit(C, op); + } + + copy_v2_v2(data->initial_stroke->init_coord, co); + + counter = 0; + + for(i = 0; i < data->totalUniqueUvs; i++){ + float dist, diff[2]; + if(data->uv[i].flag & MARK_BOUNDARY){ + continue; + } + + sub_v2_v2v2(diff, data->uv[i].uv, co); + diff[1] /= aspectRatio; + if((dist = dot_v2v2(diff, diff)) <= radius){ + float strength; + strength = alpha*brush_curve_strength(brush, sqrt(dist), radius_root); + + data->initial_stroke->initialSelection[counter].uv = i; + data->initial_stroke->initialSelection[counter].strength = strength; + copy_v2_v2(data->initial_stroke->initialSelection[counter].initial_uv, data->uv[i].uv); + counter++; + } + } + + data->initial_stroke->totalInitialSelected = counter; + } + } + + return op->customdata; +} + +static int uv_sculpt_stroke_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + UvSculptData *data; + Object *obedit = CTX_data_edit_object(C); + + if(!(data = uv_sculpt_stroke_init(C, op, event))) { + return OPERATOR_CANCELLED; + } + + uv_sculpt_stroke_apply(C, op, event, obedit); + + data->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.001f); + + if(!data->timer){ + uv_sculpt_stroke_exit(C, op); + return OPERATOR_CANCELLED; + } + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + + +static int uv_sculpt_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + UvSculptData *data = (UvSculptData *)op->customdata; + Object *obedit = CTX_data_edit_object(C); + + switch(event->type) { + case LEFTMOUSE: + case MIDDLEMOUSE: + case RIGHTMOUSE: + uv_sculpt_stroke_exit(C, op); + return OPERATOR_FINISHED; + + case MOUSEMOVE: + case INBETWEEN_MOUSEMOVE: + uv_sculpt_stroke_apply(C, op, event, obedit); + break; + case TIMER: + if(event->customdata == data->timer) + uv_sculpt_stroke_apply(C, op, event, obedit); + break; + default: + return OPERATOR_RUNNING_MODAL; + } + + ED_region_tag_redraw(CTX_wm_region(C)); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + DAG_id_tag_update(obedit->data, 0); + return OPERATOR_RUNNING_MODAL; +} + +void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Sculpt UVs"; + ot->description = "Sculpt UVs using a brush"; + ot->idname = "SCULPT_OT_uv_sculpt_stroke"; + + /* api callbacks */ + ot->invoke = uv_sculpt_stroke_invoke; + ot->modal = uv_sculpt_stroke_modal; + ot->poll = uv_sculpt_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "invert", 0, "Invert", "Inverts the operator"); + RNA_def_boolean(ot->srna, "temp_relax", 0, "Relax", "Relax Tool"); +} diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index e7a139fe465..2a8a5b3452f 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -178,6 +178,30 @@ int space_image_main_area_poll(bContext *C) return 0; } +/* For IMAGE_OT_curves_point_set to avoid sampling when in uv smooth mode */ +int space_image_main_area_not_uv_brush_poll(bContext *C) +{ + SpaceImage *sima= CTX_wm_space_image(C); + + ToolSettings *toolsettings = CTX_data_scene(C)->toolsettings; + if(sima && !toolsettings->uvsculpt) + return 1; + + return 0; +} + +static int space_image_image_sample_poll(bContext *C) +{ + SpaceImage *sima= CTX_wm_space_image(C); + Object *obedit= CTX_data_edit_object(C); + ToolSettings *toolsettings = CTX_data_scene(C)->toolsettings; + + if(obedit){ + if(ED_space_image_show_uvedit(sima, obedit) && (toolsettings->use_uv_sculpt)) + return 0; + } + return space_image_main_area_poll(C); +} /********************** view pan operator *********************/ typedef struct ViewPanData { @@ -1949,7 +1973,7 @@ void IMAGE_OT_sample(wmOperatorType *ot) ot->invoke= image_sample_invoke; ot->modal= image_sample_modal; ot->cancel= image_sample_cancel; - ot->poll= space_image_main_area_poll; + ot->poll= space_image_image_sample_poll; /* flags */ ot->flag= OPTYPE_BLOCKING; @@ -2086,7 +2110,7 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot) ot->invoke= image_sample_invoke; ot->modal= image_sample_modal; ot->cancel= image_sample_cancel; - ot->poll= space_image_main_area_poll; + ot->poll= space_image_main_area_not_uv_brush_poll; /* properties */ RNA_def_enum(ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves"); diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 6fce836af98..3d97972c829 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -770,6 +770,9 @@ static void image_main_area_init(wmWindowManager *wm, ARegion *ar) keymap= WM_keymap_find(wm->defaultconf, "UV Editor", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap= WM_keymap_find(wm->defaultconf, "UV Sculpt", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + /* own keymaps */ keymap= WM_keymap_find(wm->defaultconf, "Image Generic", SPACE_IMAGE, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); @@ -782,6 +785,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) { /* draw entirely, view changes should be handled here */ SpaceImage *sima= CTX_wm_space_image(C); + Object *obact= CTX_data_active_object(C); Object *obedit= CTX_data_edit_object(C); Scene *scene= CTX_data_scene(C); View2D *v2d= &ar->v2d; @@ -807,7 +811,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) /* and uvs in 0.0-1.0 space */ UI_view2d_view_ortho(v2d); - draw_uvedit_main(sima, ar, scene, obedit); + draw_uvedit_main(sima, ar, scene, obedit, obact); ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW); diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index d59cea3e77e..1c69e569aa6 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC uvedit_draw.c uvedit_ops.c uvedit_parametrizer.c + uvedit_smart_stitch.c uvedit_unwrap_ops.c uvedit_intern.h diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index 8f4a8ef540f..77609b9618b 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -427,12 +427,9 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, BMEditMesh *em, MTe } } -static void draw_uvs_other(Scene *scene, Object *obedit, MTexPoly *activetf) +static void draw_uvs_other(Scene *scene, Object *obedit, Image *curimage) { Base *base; - Image *curimage; - - curimage= (activetf)? activetf->tpage: NULL; glColor3ub(96, 96, 96); @@ -468,6 +465,36 @@ static void draw_uvs_other(Scene *scene, Object *obedit, MTexPoly *activetf) } } +static void draw_uvs_texpaint(SpaceImage *sima, Scene *scene, Object *ob) +{ + Mesh *me= ob->data; + Image *curimage = ED_space_image(sima); + + if(sima->flag & SI_DRAW_OTHER) + draw_uvs_other(scene, ob, curimage); + + glColor3ub(112, 112, 112); + + if(me->mtface) { + MPoly *mface= me->mpoly; + MTexPoly *tface= me->mtpoly; + MLoopUV *mloopuv; + int a, b; + + for(a=me->totpoly; a>0; a--, tface++, mface++) { + if(tface->tpage == curimage) { + glBegin(GL_LINE_LOOP); + + mloopuv = me->mloopuv + mface->loopstart; + for (b=0; b<mface->totloop; b++, mloopuv++) { + glVertex2fv(mloopuv->uv); + } + glEnd(); + } + } + } +} + /* draws uv's in the image space */ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) { @@ -485,6 +512,12 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) int drawfaces, interpedges; Image *ima= sima->image; +#if 0 /* BMESH_TODO */ + StitchPreviewer *stitch_preview = uv_get_stitch_previewer(); +#else + StitchPreviewer *stitch_preview = NULL; +#endif + em= me->edit_btmesh; activetf= EDBM_get_active_mtexpoly(em, &efa_act, 0); /* will be set to NULL if hidden */ activef = BM_get_actFace(em->bm, 0); @@ -497,8 +530,11 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) interpedges= (ts->uv_selectmode == UV_SELECT_VERTEX); /* draw other uvs */ - if(sima->flag & SI_DRAW_OTHER) - draw_uvs_other(scene, obedit, activetf); + if(sima->flag & SI_DRAW_OTHER) { + Image *curimage= (activetf)? activetf->tpage: NULL; + + draw_uvs_other(scene, obedit, curimage); + } /* 1. draw shadow mesh */ @@ -575,7 +611,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) } } - + /* 3. draw active face stippled */ if(activef) { @@ -846,23 +882,80 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) bglEnd(); } + /* finally draw stitch preview */ + if(stitch_preview) { + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + glEnableClientState(GL_VERTEX_ARRAY); + + glEnable(GL_BLEND); + + UI_ThemeColor4(TH_STITCH_PREVIEW_ACTIVE); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->static_tris); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_static_tris*3); + + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->static_quads); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_static_quads*4); + + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_tris); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + UI_ThemeColor4(TH_STITCH_PREVIEW_FACE); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + UI_ThemeColor4(TH_STITCH_PREVIEW_EDGE); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + /*UI_ThemeColor4(TH_STITCH_PREVIEW_VERT); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3);*/ + + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_quads); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + UI_ThemeColor4(TH_STITCH_PREVIEW_FACE); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + UI_ThemeColor4(TH_STITCH_PREVIEW_EDGE); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + /*UI_ThemeColor4(TH_STITCH_PREVIEW_VERT); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4);*/ + + glDisable(GL_BLEND); + + /* draw vert preview */ + glPointSize(pointsize*2.0); + UI_ThemeColor4(TH_STITCH_PREVIEW_STITCHABLE); + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_stitchable); + glDrawArrays(GL_POINTS, 0, stitch_preview->num_stitchable); + + UI_ThemeColor4(TH_STITCH_PREVIEW_UNSTITCHABLE); + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_unstitchable); + glDrawArrays(GL_POINTS, 0, stitch_preview->num_unstitchable); + + glPopClientAttrib(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + glPointSize(1.0); } -void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit) +void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit, Object *obact) { - int show_uvedit, show_uvshadow; + ToolSettings *toolsettings = scene->toolsettings; + int show_uvedit, show_uvshadow, show_texpaint_uvshadow; + show_texpaint_uvshadow = (obact && obact->type == OB_MESH && obact->mode == OB_MODE_TEXTURE_PAINT); show_uvedit= ED_space_image_show_uvedit(sima, obedit); show_uvshadow= ED_space_image_show_uvshadow(sima, obedit); - if(show_uvedit || show_uvshadow) { + if(show_uvedit || show_uvshadow || show_texpaint_uvshadow) { if(show_uvshadow) draw_uvs_shadow(obedit); - else + else if(show_uvedit) draw_uvs(sima, scene, obedit); + else + draw_uvs_texpaint(sima, scene, obact); - if(show_uvedit) + if(show_uvedit && !(toolsettings->use_uv_sculpt)) drawcursor_sima(sima, ar); } } diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index e7156c65491..58bdd8c3265 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -32,11 +32,15 @@ #ifndef ED_UVEDIT_INTERN_H #define ED_UVEDIT_INTERN_H -struct SpaceImage; +struct EditFace; +struct EditMesh; struct MTexPoly; -struct Scene; struct Image; +struct MTFace; struct Object; +struct Scene; +struct SpaceImage; +struct UvElementMap; struct wmOperatorType; struct BMEditMesh; struct BMFace; @@ -52,6 +56,7 @@ struct BMVert; int uvedit_face_visible_nolocal(struct Scene *scene, struct BMFace *efa); /* geometric utilities */ + void uv_center(float uv[][2], float cent[2], int quad); float uv_area(float uv[][2], int quad); void uv_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy); @@ -60,7 +65,55 @@ float poly_uv_area(float uv[][2], int len); void poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len); void poly_uv_center(struct BMEditMesh *em, struct BMFace *f, float cent[2]); +/* find nearest */ + +typedef struct NearestHit { + struct BMFace *efa; + struct MTexPoly *tf; + struct BMLoop *l, *nextl; + struct MLoopUV *luv, *nextluv; + int lindex; //index of loop within face + int vert1, vert2; //index in mesh of edge vertices +} NearestHit; + +void uv_find_nearest_vert(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, float co[2], float penalty[2], struct NearestHit *hit); +void uv_find_nearest_edge(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, float co[2], struct NearestHit *hit); + +/* utility tool functions */ + +struct UvElement *ED_get_uv_element(struct UvElementMap *map, struct BMFace *efa, int index); +void uvedit_live_unwrap_update(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit); + +/* smart stitch */ + +/* object that stores display data for previewing before accepting stitching */ +typedef struct StitchPreviewer { + /* OpenGL requires different calls for Triangles and Quads. + * here we'll store the quads of the mesh */ + float *preview_quads; + /* ...and here we'll store the triangles*/ + float *preview_tris; + /* preview data. These will be either the previewed vertices or edges depending on stitch mode settings */ + float *preview_stitchable; + float *preview_unstitchable; + /* here we'll store the number of triangles and quads to be drawn */ + unsigned int num_tris; + unsigned int num_quads; + unsigned int num_stitchable; + unsigned int num_unstitchable; + + /* store static island Quads */ + float *static_quads; + /* ...and here we'll store the triangles*/ + float *static_tris; + unsigned int num_static_tris; + unsigned int num_static_quads; +} StitchPreviewer; + +StitchPreviewer *uv_get_stitch_previewer(void); + /* operators */ + void UV_OT_average_islands_scale(struct wmOperatorType *ot); void UV_OT_cube_project(struct wmOperatorType *ot); void UV_OT_cylinder_project(struct wmOperatorType *ot); @@ -70,6 +123,7 @@ void UV_OT_pack_islands(struct wmOperatorType *ot); void UV_OT_reset(struct wmOperatorType *ot); void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); +void UV_OT_stitch(struct wmOperatorType *ot); #endif /* ED_UVEDIT_INTERN_H */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index a11d5935e22..c468da6b937 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -20,7 +20,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Antony Riakiotakis. * * ***** END GPL LICENSE BLOCK ***** */ @@ -103,6 +103,28 @@ int ED_uvedit_test(Object *obedit) return ret; } +static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C) +{ + SpaceImage *sima= CTX_wm_space_image(C); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + Object *obedit= CTX_data_edit_object(C); + + return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt); +} + +static int ED_operator_uvmap_mesh(bContext *C) +{ + Object *ob= CTX_data_active_object(C); + + if(ob && ob->type==OB_MESH) { + Mesh *me = ob->data; + + if(CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL) + return 1; + } + + return 0; +} /**************************** object active image *****************************/ static int is_image_texture_node(bNode *node) @@ -247,7 +269,13 @@ static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist { int width, height; - ED_space_image_size(sima, &width, &height); + if(sima) { + ED_space_image_size(sima, &width, &height); + } + else { + width= 256; + height= 256; + } dist[0]= pixeldist/width; dist[1]= pixeldist/height; @@ -468,7 +496,7 @@ void uvedit_uv_deselect(BMEditMesh *em, Scene *scene, BMLoop *l) /*********************** live unwrap utilities ***********************/ -static void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit) +void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit) { if(sima && (sima->flag & SI_LIVE_UNWRAP)) { ED_uvedit_live_unwrap_begin(scene, obedit); @@ -643,16 +671,7 @@ static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent, /************************** find nearest ****************************/ -typedef struct NearestHit { - BMFace *efa; - MTexPoly *tf; - BMLoop *l, *nextl; - MLoopUV *luv, *nextluv; - int lindex; //index of loop within face - int vert1, vert2; //index in mesh of edge vertices -} NearestHit; - -static void find_nearest_uv_edge(Scene *scene, Image *ima, BMEditMesh *em, float co[2], NearestHit *hit) +void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, float co[2], NearestHit *hit) { MTexPoly *tf; BMFace *efa; @@ -712,7 +731,7 @@ static void find_nearest_uv_face(Scene *scene, Image *ima, BMEditMesh *em, float memset(hit, 0, sizeof(*hit)); /*this will fill in hit.vert1 and hit.vert2*/ - find_nearest_uv_edge(scene, ima, em, co, hit); + uv_find_nearest_edge(scene, ima, em, co, hit); hit->l = hit->nextl = NULL; hit->luv = hit->nextluv = NULL; @@ -786,8 +805,8 @@ static int nearest_uv_between(BMEditMesh *em, BMFace *efa, int UNUSED(nverts), i return (c1*c2 >= 0.0f); } -static void find_nearest_uv_vert(Scene *scene, Image *ima, BMEditMesh *em, - float co[2], float penalty[2], NearestHit *hit) +void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em, + float co[2], float penalty[2], NearestHit *hit) { BMFace *efa; BMLoop *l; @@ -798,7 +817,7 @@ static void find_nearest_uv_vert(Scene *scene, Image *ima, BMEditMesh *em, int i; /*this will fill in hit.vert1 and hit.vert2*/ - find_nearest_uv_edge(scene, ima, em, co, hit); + uv_find_nearest_edge(scene, ima, em, co, hit); hit->l = hit->nextl = NULL; hit->luv = hit->nextluv = NULL; @@ -918,6 +937,27 @@ static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, BMFace *efa, int a) return NULL; } +/* BMESH_TODO - in some cases we already know the loop so looking up the index isnt needed */ + +UvElement *ED_get_uv_element(UvElementMap *map, BMFace *efa, int index) +{ + BMLoop *loop = efa->loops.first; + UvElement *element; + + while (index >= 0) { + loop = loop->next; + index--; + } + + element = map->vert[BM_GetIndex(loop->v)]; + + for(; element; element = element->next) + if(element->face == efa) + return element; + + return NULL; +} + static int uv_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, UvMapVert *first2, int *totface) { UvMapVert *iterv1, *iterv2; @@ -1536,6 +1576,7 @@ static void UV_OT_weld(wmOperatorType *ot) ot->poll= ED_operator_uvedit; } +#if 0 // BMESH_TODO --- this function has been moved elsewhere /* ******************** stitch operator **************** */ /* just for averaging UVs */ @@ -1703,6 +1744,8 @@ static void UV_OT_stitch(wmOperatorType *ot) RNA_def_float(ot->srna, "limit", 20.0, 0.0f, FLT_MAX, "Limit", "Limit distance in image pixels", -FLT_MAX, FLT_MAX); } +#endif + /* ******************** (de)select all operator **************** */ static void select_all_perform(bContext *C, int action) @@ -1891,7 +1934,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) /* find nearest element */ if(loop) { /* find edge */ - find_nearest_uv_edge(scene, ima, em, co, &hit); + uv_find_nearest_edge(scene, ima, em, co, &hit); if(hit.efa == NULL) { BLI_array_free(hitv); BLI_array_free(hituv); @@ -1902,7 +1945,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) } else if(selectmode == UV_SELECT_VERTEX) { /* find vertex */ - find_nearest_uv_vert(scene, ima, em, co, penalty, &hit); + uv_find_nearest_vert(scene, ima, em, co, penalty, &hit); if(hit.efa == NULL) { BLI_array_free(hitv); BLI_array_free(hituv); @@ -1923,7 +1966,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) } else if(selectmode == UV_SELECT_EDGE) { /* find edge */ - find_nearest_uv_edge(scene, ima, em, co, &hit); + uv_find_nearest_edge(scene, ima, em, co, &hit); if(hit.efa == NULL) { BLI_array_free(hitv); BLI_array_free(hituv); @@ -1973,7 +2016,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) hitlen = hit.efa->len; } else if(selectmode == UV_SELECT_ISLAND) { - find_nearest_uv_vert(scene, ima, em, co, NULL, &hit); + uv_find_nearest_vert(scene, ima, em, co, NULL, &hit); if(hit.efa==NULL) { BLI_array_free(hitv); @@ -2257,7 +2300,7 @@ static int select_linked_internal(bContext *C, wmOperator *op, wmEvent *event, i RNA_float_get_array(op->ptr, "location", co); } - find_nearest_uv_vert(scene, ima, em, co, NULL, &hit); + uv_find_nearest_vert(scene, ima, em, co, NULL, &hit); hit_p= &hit; } @@ -3457,6 +3500,183 @@ static void UV_OT_tile_set(wmOperatorType *ot) RNA_def_int_vector(ot->srna, "tile", 2, NULL, 0, INT_MAX, "Tile", "Tile coordinate", 0, 10); } + +static int seams_from_islands_exec(bContext *C, wmOperator *op) +{ + UvVertMap *vmap; + Object *ob = CTX_data_edit_object(C); + Mesh *me= (Mesh*)ob->data; + BMEditMesh *em; + BMEdge *editedge; + float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT}; + char mark_seams = RNA_boolean_get(op->ptr, "mark_seams"); + char mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp"); + + BMesh *bm; + BMIter iter; + + em = me->edit_btmesh; + bm = em->bm; + + if(!EDBM_texFaceCheck(em)) { + return OPERATOR_CANCELLED; + } + + /* This code sets editvert->tmp.l to the index. This will be useful later on. */ + EDBM_init_index_arrays(em, 0, 0, 1); + vmap = EDBM_make_uv_vert_map(em, 0, 0, limit); + + BM_ITER(editedge, &iter, bm, BM_EDGES_OF_MESH, NULL) { + /* flags to determine if we uv is separated from first editface match */ + char separated1 = 0, separated2; + /* set to denote edge must be flagged as seam */ + char faces_separated = 0; + /* flag to keep track if uv1 is disconnected from first editface match */ + char v1coincident = 1; + /* For use with v1coincident. v1coincident will change only if we've had commonFaces */ + int commonFaces = 0; + + BMFace *efa1, *efa2; + + UvMapVert *mv1, *mvinit1, *mv2, *mvinit2, *mviter; + /* mv2cache stores the first of the list of coincident uv's for later comparison + * mv2sep holds the last separator and is copied to mv2cache when a hit is first found */ + UvMapVert *mv2cache = NULL, *mv2sep = NULL; + + mvinit1 = vmap->vert[BM_GetIndex(editedge->v1)]; + if(mark_seams) + BM_ClearHFlag(editedge, BM_SEAM); + + for(mv1 = mvinit1; mv1 && !faces_separated; mv1 = mv1->next) { + if(mv1->separate && commonFaces) + v1coincident = 0; + + separated2 = 0; + efa1 = EDBM_get_face_for_index(em, mv1->f); + mvinit2 = vmap->vert[BM_GetIndex(editedge->v2)]; + + for(mv2 = mvinit2; mv2; mv2 = mv2->next) { + if(mv2->separate) + mv2sep = mv2; + + efa2 = EDBM_get_face_for_index(em, mv2->f); + if(efa1 == efa2) { + /* if v1 is not coincident no point in comparing */ + if(v1coincident) { + /* have we found previously anything? */ + if(mv2cache) { + /* flag seam unless proved to be coincident with previous hit */ + separated2 = 1; + for(mviter = mv2cache; mviter; mviter = mviter->next) { + if(mviter->separate && mviter != mv2cache) + break; + /* coincident with previous hit, do not flag seam */ + if(mviter == mv2) + separated2 = 0; + } + } + /* First hit case, store the hit in the cache */ + else { + mv2cache = mv2sep; + commonFaces = 1; + } + } + else + separated1 = 1; + + if(separated1 || separated2) { + faces_separated = 1; + break; + } + } + } + } + + if(faces_separated) { + if(mark_seams) + BM_SetHFlag(editedge, BM_SEAM); + if(mark_sharp) + BM_SetHFlag(editedge, BM_SHARP); + } + } + + me->drawflag |= ME_DRAWSEAMS; + + EDBM_free_uv_vert_map(vmap); + EDBM_free_index_arrays(em); + + DAG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); + + return OPERATOR_FINISHED; +} + + +static void UV_OT_seams_from_islands(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Seams From Islands"; + ot->description= "Set mesh seams according to island setup in the UV editor"; + ot->idname= "UV_OT_seams_from_islands"; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* api callbacks */ + ot->exec= seams_from_islands_exec; + ot->poll= ED_operator_uvedit; + + RNA_def_boolean(ot->srna, "mark_seams", 1, "Mark Seams", "Mark boundary edges as seams"); + RNA_def_boolean(ot->srna, "mark_sharp", 0, "Mark Sharp", "Mark boundary edges as sharp"); +} + +static int mark_seam_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + Mesh *me= (Mesh*)ob->data; + BMEditMesh *em= me->edit_btmesh; + BMesh *bm = em->bm; + BMFace *efa; + BMLoop *loop; + + BMIter iter, liter; + + BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { + BM_ITER(loop, &liter, bm, BM_LOOPS_OF_FACE, efa) { + if(uvedit_edge_selected(em, scene, loop)) { + BM_SetHFlag(loop, BM_SEAM); + } + } + } + + me->drawflag |= ME_DRAWSEAMS; + + if(scene->toolsettings->edge_mode_live_unwrap) + ED_unwrap_lscm(scene, ob, FALSE); + + DAG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); + + return OPERATOR_FINISHED; +} + +static void UV_OT_mark_seam(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mark Seams"; + ot->description= "Mark selected UV edges as seams"; + ot->idname= "UV_OT_mark_seam"; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* api callbacks */ + ot->exec= mark_seam_exec; + ot->poll= ED_operator_uvedit; +} + + /* ************************** registration **********************************/ void ED_operatortypes_uvedit(void) @@ -3475,7 +3695,11 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_snap_selected); WM_operatortype_append(UV_OT_align); +#if 0 /* BMESH_TODO */ WM_operatortype_append(UV_OT_stitch); +#endif + WM_operatortype_append(UV_OT_seams_from_islands); + WM_operatortype_append(UV_OT_mark_seam); WM_operatortype_append(UV_OT_weld); WM_operatortype_append(UV_OT_pin); @@ -3502,7 +3726,14 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf) wmKeyMapItem *kmi; keymap= WM_keymap_find(keyconf, "UV Editor", 0, 0); - keymap->poll= ED_operator_uvedit; + keymap->poll= ED_operator_uvedit_can_uv_sculpt; + + /* Uv sculpt toggle */ + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt"); + + /* Mark edge seam */ + WM_keymap_add_item(keymap, "UV_OT_mark_seam", EKEY, KM_PRESS, KM_CTRL, 0); /* pick selection */ RNA_boolean_set(WM_keymap_add_item(keymap, "UV_OT_select", SELECTMOUSE, KM_PRESS, 0, 0)->ptr, "extend", FALSE); diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 9bb4d655a59..68c8669ccc7 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -91,7 +91,7 @@ typedef struct PVert { } u; struct PEdge *edge; - float *co; + float co[3]; float uv[2]; unsigned char flag; @@ -655,11 +655,15 @@ static void p_face_backup_uvs(PFace *f) { PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; - if (e1->orig_uv && e2->orig_uv && e3->orig_uv) { + if (e1->orig_uv) { e1->old_uv[0] = e1->orig_uv[0]; e1->old_uv[1] = e1->orig_uv[1]; + } + if (e2->orig_uv) { e2->old_uv[0] = e2->orig_uv[0]; e2->old_uv[1] = e2->orig_uv[1]; + } + if (e3->orig_uv) { e3->old_uv[0] = e3->orig_uv[0]; e3->old_uv[1] = e3->orig_uv[1]; } @@ -669,11 +673,15 @@ static void p_face_restore_uvs(PFace *f) { PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; - if (e1->orig_uv && e2->orig_uv && e3->orig_uv) { + if (e1->orig_uv) { e1->orig_uv[0] = e1->old_uv[0]; e1->orig_uv[1] = e1->old_uv[1]; + } + if (e2->orig_uv) { e2->orig_uv[0] = e2->old_uv[0]; e2->orig_uv[1] = e2->old_uv[1]; + } + if (e3->orig_uv) { e3->orig_uv[0] = e3->old_uv[0]; e3->orig_uv[1] = e3->old_uv[1]; } @@ -684,7 +692,7 @@ static void p_face_restore_uvs(PFace *f) static PVert *p_vert_add(PHandle *handle, PHashKey key, float *co, PEdge *e) { PVert *v = (PVert*)BLI_memarena_alloc(handle->arena, sizeof *v); - v->co = co; + copy_v3_v3(v->co, co); v->u.key = key; v->edge = e; v->flag = 0; @@ -708,7 +716,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v) { PVert *nv = (PVert*)BLI_memarena_alloc(chart->handle->arena, sizeof *nv); - nv->co = v->co; + copy_v3_v3(nv->co, v->co); nv->uv[0] = v->uv[0]; nv->uv[1] = v->uv[1]; nv->u.key = v->u.key; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c new file mode 100644 index 00000000000..cc139544ce3 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -0,0 +1,1446 @@ + +#if 0 /* BMESH TODO */ + +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Antony Riakiotakis. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/uvedit/uvedit_stitch.c + * \ingroup eduv + */ + + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" + +#include "BLI_editVert.h" +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_mesh.h" +#include "BKE_tessmesh.h" + +#include "ED_mesh.h" +#include "ED_uvedit.h" +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "uvedit_intern.h" + +/* ********************** smart stitch operator *********************** */ + + +struct IslandStitchData; + +/* This is a straightforward implementation, count the uv's in the island that will move and take the mean displacement/rotation and apply it to all + * elements of the island except from the stitchable */ +typedef struct IslandStitchData{ + /* rotation can be used only for edges, for vertices there is no such notion */ + float rotation; + float translation[2]; + /* Used for rotation, the island will rotate around this point */ + float medianPoint[2]; + int numOfElements; + int num_rot_elements; + /* Flag to remember if island has been added for preview */ + char addedForPreview; + /* Flag an island to be considered for determining static island */ + char stitchableCandidate; +}IslandStitchData; + +/* just for averaging UVs */ +typedef struct UVVertAverage { + float uv[2]; + unsigned short count; +} UVVertAverage; + +typedef struct UvEdge { + /* index to uv buffer */ + unsigned int uv1; + unsigned int uv2; + /* general use flag (Used to check if edge is boundary here, and propagates to adjacency elements) */ + char flag; + /* element that guarantees element->face has the face on element->tfindex and element->tfindex+1 is the second uv */ + UvElement *element; +}UvEdge; + + +/* stitch state object */ +typedef struct StitchState { + /* use limit flag */ + char use_limit; + /* limit to operator, same as original operator */ + float limit_dist; + /* snap uv islands together during stitching */ + char snap_islands; + /* stich at midpoints or at islands */ + char midpoints; + /* editmesh, cached for use in modal handler */ + EditMesh *em; + /* element map for getting info about uv connectivity */ + UvElementMap *element_map; + /* edge container */ + UvEdge *uvedges; + /* container of first of a group of coincident uvs, these will be operated upon */ + UvElement **uvs; + /* maps uvelements to their first coincident uv */ + int *map; + /* 2D normals per uv to calculate rotation for snapping */ + float *normals; + /* edge storage */ + UvEdge *edges; + + /* count of separate uvs and edges */ + int total_boundary_edges; + int total_separate_uvs; + /* hold selection related information */ + UvElement **selection_stack; + int selection_size; + /* island that stays in place */ + int static_island; + /* store number of primitives per face so that we can allocate the active island buffer later */ + unsigned int *quads_per_island; + unsigned int *tris_per_island; +} StitchState; + + +/* + * defines for UvElement flags + */ +#define STITCH_SELECTED 1 +#define STITCH_STITCHABLE 2 +#define STITCH_PROCESSED 4 +#define STITCH_BOUNDARY 8 +#define STITCH_STITCHABLE_CANDIDATE 16 + +#define STITCH_NO_PREVIEW -1 + +/* previewer stuff (see uvedit_intern.h for more info) */ +static StitchPreviewer *_stitch_preview; + +/* constructor */ +static StitchPreviewer * stitch_preview_init(void) +{ + _stitch_preview = MEM_mallocN(sizeof(StitchPreviewer), "stitch_previewer"); + _stitch_preview->preview_quads = NULL; + _stitch_preview->preview_tris = NULL; + _stitch_preview->preview_stitchable = NULL; + _stitch_preview->preview_unstitchable = NULL; + + _stitch_preview->num_quads = 0; + _stitch_preview->num_tris = 0; + _stitch_preview->num_stitchable = 0; + _stitch_preview->num_unstitchable = 0; + + _stitch_preview->static_quads = NULL; + _stitch_preview->static_tris = NULL; + + _stitch_preview->num_static_tris = 0; + _stitch_preview->num_static_quads = 0; + + return _stitch_preview; +} + +/* destructor...yeah this should be C++ :) */ +static void stitch_preview_delete(void) +{ + if(_stitch_preview) + { + if(_stitch_preview->preview_quads){ + MEM_freeN(_stitch_preview->preview_quads); + _stitch_preview->preview_quads = NULL; + } + if(_stitch_preview->preview_tris){ + MEM_freeN(_stitch_preview->preview_tris); + _stitch_preview->preview_tris = NULL; + } + if(_stitch_preview->preview_stitchable){ + MEM_freeN(_stitch_preview->preview_stitchable); + _stitch_preview->preview_stitchable = NULL; + } + if(_stitch_preview->preview_unstitchable){ + MEM_freeN(_stitch_preview->preview_unstitchable); + _stitch_preview->preview_unstitchable = NULL; + } + if(_stitch_preview->static_quads){ + MEM_freeN(_stitch_preview->static_quads); + _stitch_preview->static_quads = NULL; + } + if(_stitch_preview->static_tris){ + MEM_freeN(_stitch_preview->static_tris); + _stitch_preview->static_tris = NULL; + } + MEM_freeN(_stitch_preview); + _stitch_preview = NULL; + } +} + + +/* "getter method" */ +StitchPreviewer *uv_get_stitch_previewer(void) +{ + return _stitch_preview; +} + +#define HEADER_LENGTH 256 + +/* This function updates the header of the UV editor when the stitch tool updates its settings */ +static void stitch_update_header(StitchState *stitch_state, bContext *C) +{ + static char str[] = "(S)nap %s, (M)idpoints %s, (L)imit %.2f (Alt Wheel adjust) %s, Switch (I)sland, shift select vertices"; + + char msg[HEADER_LENGTH]; + ScrArea *sa= CTX_wm_area(C); + + if(sa) { + BLI_snprintf(msg, HEADER_LENGTH, str, + stitch_state->snap_islands? "On" : "Off", + stitch_state->midpoints? "On": "Off", + stitch_state->limit_dist, + stitch_state->use_limit? "On" : "Off"); + + ED_area_headerprint(sa, msg); + } +} + +static int getNumOfIslandUvs(UvElementMap *elementMap, int island){ + if(island == elementMap->totalIslands-1){ + return elementMap->totalUVs - elementMap->islandIndices[island]; + }else{ + return elementMap->islandIndices[island+1] - elementMap->islandIndices[island]; + } +} + +static void stitch_uv_rotate(float rotation, float medianPoint[2], float uv[2]){ + float uv_rotation_result[2]; + + uv[0] -= medianPoint[0]; + uv[1] -= medianPoint[1]; + + uv_rotation_result[0] = cos(rotation)*uv[0] - sin(rotation)*uv[1]; + uv_rotation_result[1] = sin(rotation)*uv[0] + cos(rotation)*uv[1]; + + uv[0] = uv_rotation_result[0] + medianPoint[0]; + uv[1] = uv_rotation_result[1] + medianPoint[1]; +} + + +/* calculate snapping for islands */ +static void stitch_calculate_island_snapping(StitchState *state, StitchPreviewer *preview, IslandStitchData *island_stitch_data, int final){ + int i; + EditFace *efa; + MTFace *mt; + UvElement *element; + + for(i = 0; i < state->element_map->totalIslands; i++){ + if(island_stitch_data[i].addedForPreview){ + int numOfIslandUVs = 0, j; + + /* check to avoid divide by 0 */ + if(island_stitch_data[i].num_rot_elements>0){ + island_stitch_data[i].rotation /= island_stitch_data[i].num_rot_elements; + island_stitch_data[i].medianPoint[0] /= island_stitch_data[i].numOfElements; + island_stitch_data[i].medianPoint[1] /= island_stitch_data[i].numOfElements; + } + island_stitch_data[i].translation[0] /= island_stitch_data[i].numOfElements; + island_stitch_data[i].translation[1] /= island_stitch_data[i].numOfElements; + numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); + element = &state->element_map->buf[state->element_map->islandIndices[i]]; + for(j = 0; j < numOfIslandUVs; j++, element++){ + /* stitchable uvs have already been processed, don't process */ + if(!(element->flag & STITCH_PROCESSED)){ + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + if(final){ + + stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, mt->uv[element->tfindex]); + + mt->uv[element->tfindex][0] += island_stitch_data[i].translation[0]; + mt->uv[element->tfindex][1] += island_stitch_data[i].translation[1]; + } + else if(efa->tmp.l != STITCH_NO_PREVIEW){ + if(efa->v4){ + + stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, &preview->preview_quads[efa->tmp.l + 2*element->tfindex]); + + preview->preview_quads[efa->tmp.l + 2*element->tfindex] += island_stitch_data[i].translation[0]; + preview->preview_quads[efa->tmp.l + 2*element->tfindex + 1] += island_stitch_data[i].translation[1]; + } + else { + + stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, &preview->preview_tris[efa->tmp.l + 2*element->tfindex]); + + preview->preview_tris[efa->tmp.l + 2*element->tfindex] += island_stitch_data[i].translation[0]; + preview->preview_tris[efa->tmp.l + 2*element->tfindex + 1] += island_stitch_data[i].translation[1]; + } + } + } + /* cleanup */ + element->flag &= STITCH_SELECTED; + } + } + } +} + + + +static void stitch_island_calculate_edge_rotation(UvEdge *edge, StitchState *state, UVVertAverage *uv_average, unsigned int *uvfinal_map, IslandStitchData *island_stitch_data) +{ + UvElement *element; + EditFace *efa; + MTFace *mt; + int nverts; + float uv1[2], uv2[2]; + float edgecos, edgesin; + int index1, index2; + + element = edge->element; + efa = element->face; + nverts = (efa->v4)? 4 : 3; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + index1 = uvfinal_map[(*(&element->face->v1 + element->tfindex))->tmp.l]; + index2 = uvfinal_map[(*(&element->face->v1 + (element->tfindex + 1)%nverts))->tmp.l]; + + /* the idea here is to take the directions of the edges and find the rotation between final and initial + * direction. This, using inner and outer vector products, gives the angle. Directions are differences so... */ + uv1[0] = mt->uv[(element->tfindex + 1)%nverts][0] - mt->uv[element->tfindex][0]; + uv1[1] = mt->uv[(element->tfindex + 1)%nverts][1] - mt->uv[element->tfindex][1]; + + uv2[0] = uv_average[index2].uv[0] - uv_average[index1].uv[0]; + uv2[1] = uv_average[index2].uv[1] - uv_average[index1].uv[1]; + + normalize_v2(uv1); + normalize_v2(uv2); + + edgecos = uv1[0]*uv2[0] + uv1[1]*uv2[1]; + edgesin = uv1[0]*uv2[1] - uv2[0]*uv1[1]; + island_stitch_data[element->island].num_rot_elements++; + island_stitch_data[element->island].rotation += (edgesin > 0)? acos(MAX2(-1.0, MIN2(1.0, edgecos))): -acos(MAX2(-1.0, MIN2(1.0, edgecos))); +} + +static void stitch_island_calculate_vert_rotation(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data, char do_static) +{ + float edgecos = 1, edgesin = 0; + int index; + UvElement *element_iter; + + if((element->island == state->static_island) && !do_static) + return; + + index = (*(&element->face->v1 + element->tfindex))->tmp.l; + + element_iter = state->element_map->vert[index]; + + if(!do_static){ + for(; element_iter; element_iter = element_iter->next){ + if((element_iter->separate) && (element_iter->flag & STITCH_STITCHABLE) && + (element_iter != element) && (element_iter->island == state->static_island) + ){ + int index_tmp1, index_tmp2; + float normal[2]; + /* easily possible*/ + + index_tmp1 = element_iter - state->element_map->buf; + index_tmp1 = state->map[index_tmp1]; + index_tmp2 = element - state->element_map->buf; + index_tmp2 = state->map[index_tmp2]; + + negate_v2_v2(normal, state->normals + index_tmp2*2); + edgecos = dot_v2v2(normal, state->normals + index_tmp1*2); + edgesin = cross_v2v2(normal, state->normals + index_tmp1*2); + break; + } + } + } + + island_stitch_data[element->island].num_rot_elements++; + island_stitch_data[element->island].rotation += (edgesin > 0)? acos(edgecos): -acos(edgecos); +} + + +static void stitch_state_delete(StitchState *stitch_state) +{ + if(stitch_state){ + if(stitch_state->element_map){ + EDBM_free_uv_element_map(stitch_state->element_map); + } + if(stitch_state->uvs){ + MEM_freeN(stitch_state->uvs); + } + if(stitch_state->selection_stack){ + MEM_freeN(stitch_state->selection_stack); + } + if(stitch_state->quads_per_island){ + MEM_freeN(stitch_state->quads_per_island); + } + if(stitch_state->tris_per_island){ + MEM_freeN(stitch_state->tris_per_island); + } + if(stitch_state->map){ + MEM_freeN(stitch_state->map); + } + if(stitch_state->normals){ + MEM_freeN(stitch_state->normals); + } + if(stitch_state->edges){ + MEM_freeN(stitch_state->edges); + } + MEM_freeN(stitch_state); + } +} + + + +/* checks for remote uvs that may be stitched with a certain uv, flags them if stitchable. */ +static void determine_uv_stitchability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){ + int vert_index; + UvElement *element_iter; + float limit= state->limit_dist; + int do_limit = state->use_limit; + + vert_index = (*(&element->face->v1 + element->tfindex))->tmp.l; + element_iter = state->element_map->vert[vert_index]; + + for(; element_iter; element_iter = element_iter->next){ + if(element_iter->separate){ + if(element_iter == element){ + continue; + } + if(do_limit){ + MTFace *mtface_orig = CustomData_em_get(&state->em->fdata, element->face->data, CD_MTFACE); + MTFace *mtface_iter = CustomData_em_get(&state->em->fdata, element_iter->face->data, CD_MTFACE); + + if(fabs(mtface_orig->uv[element->tfindex][0] - mtface_iter->uv[element_iter->tfindex][0]) < limit + && fabs(mtface_orig->uv[element->tfindex][1] - mtface_iter->uv[element_iter->tfindex][1]) < limit){ + island_stitch_data[element_iter->island].stitchableCandidate = 1; + island_stitch_data[element->island].stitchableCandidate = 1; + element->flag |= STITCH_STITCHABLE_CANDIDATE; + } + }else{ + /* if no limit exists, then the mere existence of a separate uv means that the uv is stitchable */ + island_stitch_data[element_iter->island].stitchableCandidate = 1; + island_stitch_data[element->island].stitchableCandidate = 1; + element->flag |= STITCH_STITCHABLE_CANDIDATE; + } + } + } +} + + + +/* set preview buffer position of UV face in editface->tmp.l */ +static void stitch_set_face_preview_buffer_position(EditFace *efa, StitchPreviewer *preview) +{ + if(efa->tmp.l == STITCH_NO_PREVIEW) + { + if(efa->v4) + { + efa->tmp.l = preview->num_quads*8; + preview->num_quads++; + } else { + efa->tmp.l = preview->num_tris*6; + preview->num_tris++; + } + } +} + + +/* setup face preview for all coincident uvs and their faces */ +static void stitch_setup_face_preview_for_uv_group(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){ + StitchPreviewer *preview = uv_get_stitch_previewer(); + + /* static island does not change so returning immediately */ + //if(state->snap_islands && !state->midpoints && state->static_island == element->island) + // return; + + if(state->snap_islands){ + island_stitch_data[element->island].addedForPreview = 1; + } + + do{ + stitch_set_face_preview_buffer_position(element->face, preview); + element = element->next; + }while(element && !element->separate); +} + + +/* checks if uvs are indeed stitchable and registers so that they can be shown in preview */ +static void stitch_validate_stichability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){ + UvElement *element_iter; + StitchPreviewer *preview; + + preview = uv_get_stitch_previewer(); + element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + + for(; element_iter; element_iter = element_iter->next){ + if(element_iter->separate){ + if(element_iter == element) + continue; + if(state->use_limit){ + MTFace *mtface_orig = CustomData_em_get(&state->em->fdata, element->face->data, CD_MTFACE); + MTFace *mtface_iter = CustomData_em_get(&state->em->fdata, element_iter->face->data, CD_MTFACE); + + if(fabs(mtface_orig->uv[element->tfindex][0] - mtface_iter->uv[element_iter->tfindex][0]) < state->limit_dist + && fabs(mtface_orig->uv[element->tfindex][1] - mtface_iter->uv[element_iter->tfindex][1]) < state->limit_dist){ + if(((element_iter->island == state->static_island) || (element->island == state->static_island)) && + !((element_iter->island == element->island) && state->snap_islands)){ + element->flag |= STITCH_STITCHABLE; + preview->num_stitchable++; + stitch_setup_face_preview_for_uv_group(element, state, island_stitch_data); + return; + } + } + }else{ + if(((element_iter->island == state->static_island) || (element->island == state->static_island)) && + !((element_iter->island == element->island) && state->snap_islands)){ + element->flag |= STITCH_STITCHABLE; + preview->num_stitchable++; + stitch_setup_face_preview_for_uv_group(element, state, island_stitch_data); + return; + } + } + } + } + + /* this can happen if the uvs to be stitched are not on a stitchable island */ + if(!(element->flag & STITCH_STITCHABLE)){ + preview->num_unstitchable++; + } +} + +/* main processing function. It calculates preview and final positions. */ +static int stitch_process_data(StitchState *state, Scene *scene, int final) +{ + int i; + StitchPreviewer *preview = uv_get_stitch_previewer(); + IslandStitchData *island_stitch_data = NULL; + int previous_island = state->static_island; + EditFace *efa; + EditVert *ev; + UVVertAverage *final_position; + char stitch_midpoints = state->midpoints; + /* use vertex normals for snapping rotation */ + char use_vert_normals = 1; + /* used to map uv indices to uvaverage indices for selection */ + unsigned int *uvfinal_map; + + /* cleanup previous preview */ + stitch_preview_delete(); + preview = stitch_preview_init(); + if(preview == NULL) + return 0; + /* each face holds its position in the preview buffer in tmp. -1 is uninitialized */ + for(efa = state->em->faces.first; efa; efa = efa->next){ + efa->tmp.l = STITCH_NO_PREVIEW; + } + + island_stitch_data = MEM_callocN(sizeof(*island_stitch_data)*state->element_map->totalIslands, "stitch_island_data"); + if(!island_stitch_data){ + return 0; + } + + /* store Indices to editVerts. */ + for(ev = state->em->verts.first, i = 0; ev; ev = ev->next, i++){ + ev->tmp.l = i; + } + + /***************************************** + * First determine stitchability of uvs * + *****************************************/ + + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + determine_uv_stitchability(element, state, island_stitch_data); + } + + /* set static island to one that is added for preview */ + state->static_island %= state->element_map->totalIslands; + while(!(island_stitch_data[state->static_island].stitchableCandidate)){ + state->static_island++; + state->static_island %= state->element_map->totalIslands; + /* this is entirely possible if for example limit stitching with no stitchable verts or no selection */ + if(state->static_island == previous_island) + break; + } + + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE_CANDIDATE){ + element->flag &= ~STITCH_STITCHABLE_CANDIDATE; + stitch_validate_stichability(element, state, island_stitch_data); + }else{ + /* add to preview for unstitchable */ + preview->num_unstitchable++; + } + } + + /***************************************** + * Setup preview for stitchable islands * + *****************************************/ + if(state->snap_islands){ + for(i = 0; i < state->element_map->totalIslands; i++){ + if(island_stitch_data[i].addedForPreview){ + int numOfIslandUVs = 0, j; + UvElement *element; + numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); + element = &state->element_map->buf[state->element_map->islandIndices[i]]; + for(j = 0; j < numOfIslandUVs; j++, element++){ + stitch_set_face_preview_buffer_position(element->face, preview); + } + } + } + } + + /********************************************************************* + * Setup the preview buffers and fill them with the appropriate data * + *********************************************************************/ + if(!final){ + unsigned int tricount = 0, quadcount = 0; + int stitchBufferIndex = 0, unstitchBufferIndex = 0; + /* initialize the preview buffers */ + preview->preview_quads = (float *)MEM_mallocN(preview->num_quads*sizeof(float)*8, "quad_uv_stitch_prev"); + preview->preview_tris = (float *)MEM_mallocN(preview->num_tris*sizeof(float)*6, "tri_uv_stitch_prev"); + + preview->preview_stitchable = (float *)MEM_mallocN(preview->num_stitchable*sizeof(float)*2, "stitch_preview_stichable_data"); + preview->preview_unstitchable = (float *)MEM_mallocN(preview->num_unstitchable*sizeof(float)*2, "stitch_preview_unstichable_data"); + + preview->static_quads = (float *)MEM_mallocN(state->quads_per_island[state->static_island]*sizeof(float)*8, "static_island_preview_quads"); + preview->static_tris = (float *)MEM_mallocN(state->tris_per_island[state->static_island]*sizeof(float)*6, "static_island_preview_tris"); + + preview->num_static_quads = state->quads_per_island[state->static_island]; + preview->num_static_tris = state->tris_per_island[state->static_island]; + /* will cause cancel and freeing of all data structures so OK */ + if(!preview->preview_quads || !preview->preview_tris || !preview->preview_stitchable || !preview->preview_unstitchable){ + return 0; + } + + /* copy data from MTFaces to the preview display buffers */ + for(efa = state->em->faces.first; efa; efa = efa->next){ + MTFace *mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + UvElement *element = ED_get_uv_element(state->element_map, efa, 0); + + if(element){ + if(efa->tmp.l != STITCH_NO_PREVIEW){ + if(efa->v4) { + memcpy(preview->preview_quads+efa->tmp.l, &mt->uv[0][0], 8*sizeof(float)); + } else { + memcpy(preview->preview_tris+efa->tmp.l, &mt->uv[0][0], 6*sizeof(float)); + } + } + + if(element->island == state->static_island){ + if(efa->v4) { + memcpy(preview->static_quads + quadcount*8, &mt->uv[0][0], 8*sizeof(float)); + quadcount++; + } else { + memcpy(preview->static_tris + tricount*6, &mt->uv[0][0], 6*sizeof(float)); + tricount++; + } + } + } + } + + /* fill the appropriate preview buffers */ + for(i = 0; i < state->total_separate_uvs; i++){ + UvElement *element = (UvElement *)state->uvs[i]; + if(element->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + preview->preview_stitchable[stitchBufferIndex*2] = mt->uv[element->tfindex][0]; + preview->preview_stitchable[stitchBufferIndex*2 + 1] = mt->uv[element->tfindex][1]; + stitchBufferIndex++; + } + else if(element->flag & STITCH_SELECTED){ + MTFace *mt; + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + preview->preview_unstitchable[unstitchBufferIndex*2] = mt->uv[element->tfindex][0]; + preview->preview_unstitchable[unstitchBufferIndex*2 + 1] = mt->uv[element->tfindex][1]; + unstitchBufferIndex++; + } + } + } + + /****************************************************** + * Here we calculate the final coordinates of the uvs * + ******************************************************/ + + final_position = MEM_callocN(state->selection_size*sizeof(*final_position), "stitch_uv_average"); + uvfinal_map = MEM_mallocN(state->em->totvert*sizeof(*uvfinal_map), "stitch_uv_final_map"); + + /* first pass, calculate final position for stitchable uvs of the static island */ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + UvElement *element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + uvfinal_map[(*(&element->face->v1 + element->tfindex))->tmp.l] = i; + for(;element_iter; element_iter = element_iter->next){ + if(element_iter->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element_iter->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + if(stitch_midpoints){ + final_position[i].uv[0] += mt->uv[element_iter->tfindex][0]; + final_position[i].uv[1] += mt->uv[element_iter->tfindex][1]; + final_position[i].count++; + }else if(element_iter->island == state->static_island){ + final_position[i].uv[0] = mt->uv[element_iter->tfindex][0]; + final_position[i].uv[1] = mt->uv[element_iter->tfindex][1]; + } + } + } + } + if(stitch_midpoints){ + final_position[i].uv[0] /= final_position[i].count; + final_position[i].uv[1] /= final_position[i].count; + } + } + + /* second pass, calculate island rotation and translation before modifying any uvs */ + if(state->snap_islands){ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + /* accumulate each islands' translation from stitchable elements. it is important to do here + * because in final pass MTFaces get modified and result is zero. */ + island_stitch_data[element->island].translation[0] += final_position[i].uv[0] - mt->uv[element->tfindex][0]; + island_stitch_data[element->island].translation[1] += final_position[i].uv[1] - mt->uv[element->tfindex][1]; + island_stitch_data[element->island].medianPoint[0] += mt->uv[element->tfindex][0]; + island_stitch_data[element->island].medianPoint[1] += mt->uv[element->tfindex][1]; + island_stitch_data[element->island].numOfElements++; + } + } + + /* only calculate rotation when an edge has been fully selected */ + for(i = 0; i < state->total_boundary_edges; i++){ + UvEdge *edge = state->edges+i; + if((state->uvs[edge->uv1]->flag & STITCH_STITCHABLE) && (state->uvs[edge->uv2]->flag & STITCH_STITCHABLE)){ + stitch_island_calculate_edge_rotation(edge, state, final_position, uvfinal_map, island_stitch_data); + use_vert_normals = 0; + } + } + if(use_vert_normals){ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + stitch_island_calculate_vert_rotation(element, state, island_stitch_data, 0); + } + } + } + } + + /* third pass, propagate changes to stitchable uvs */ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + UvElement *element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + for(;element_iter;){ + /* determine if uv stitchable */ + if(element_iter->separate && element_iter->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element_iter->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + /* propagate to coincident uvs */ + do{ + efa = element_iter->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + element_iter->flag |= STITCH_PROCESSED; + /* either flush to preview or to the MTFace, if final */ + if(final){ + mt->uv[element_iter->tfindex][0] = final_position[i].uv[0]; + mt->uv[element_iter->tfindex][1] = final_position[i].uv[1]; + + uvedit_uv_select(scene, efa, mt, element_iter->tfindex); + }else if(efa->tmp.l != STITCH_NO_PREVIEW){ + if(efa->v4){ + *(preview->preview_quads+efa->tmp.l + element_iter->tfindex*2) = final_position[i].uv[0]; + *(preview->preview_quads+efa->tmp.l + element_iter->tfindex*2 + 1) = final_position[i].uv[1]; + }else{ + *(preview->preview_tris+efa->tmp.l + element_iter->tfindex*2) = final_position[i].uv[0]; + *(preview->preview_tris+efa->tmp.l + element_iter->tfindex*2 + 1) = final_position[i].uv[1]; + } + } + + /* end of calculations, keep only the selection flag */ + if( (!state->snap_islands) || ((!stitch_midpoints) && (element_iter->island == state->static_island))) { + element_iter->flag &= STITCH_SELECTED; + } + + element_iter = element_iter->next; + }while(element_iter && !element_iter->separate); + + continue; + } + element_iter = element_iter->next; + } + } + } + + /* final pass, calculate Island translation/rotation if needed */ + if(state->snap_islands){ + stitch_calculate_island_snapping(state, preview, island_stitch_data, final); + } + + MEM_freeN(final_position); + MEM_freeN(uvfinal_map); + MEM_freeN(island_stitch_data); + + return 1; +} + +/* Stitch hash initialisation functions */ +static unsigned int uv_edge_hash(const void *key){ + UvEdge *edge = (UvEdge *)key; + return + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv2)) + + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv1)); +} + +static int uv_edge_compare(const void *a, const void *b){ + UvEdge *edge1 = (UvEdge *)a; + UvEdge *edge2 = (UvEdge *)b; + + if((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)){ + return 0; + } + return 1; +} + + +/* Select all common uvs */ +static void stitch_select_uv(UvElement *element, StitchState *stitch_state, int always_select) +{ + /* This works due to setting of tmp in find nearest uv vert */ + UvElement *element_iter; + UvElement **selection_stack = stitch_state->selection_stack; + + element_iter = stitch_state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + /* first deselect all common uvs */ + for(; element_iter; element_iter = element_iter->next){ + if(element_iter->separate){ + /* only separators go to selection */ + if(element_iter->flag & STITCH_SELECTED){ + int i; + if(always_select) + continue; + + element_iter->flag &= ~STITCH_SELECTED; + for(i = 0; i < stitch_state->selection_size; i++){ + if(selection_stack[i] == element_iter){ + (stitch_state->selection_size)--; + selection_stack[i] = selection_stack[stitch_state->selection_size]; + break; + } + } + }else{ + element_iter->flag |= STITCH_SELECTED; + selection_stack[(stitch_state->selection_size)++] = element_iter; + } + } + } +} + +static void stitch_calculate_edge_normal(EditMesh *em, UvEdge *edge, float *normal){ + UvElement *element = edge->element; + EditFace *efa = element->face; + MTFace *mt = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + int nverts = efa->v4?4 : 3; + int index = index = (element->tfindex + 2)%nverts; + float tangent[2], internal[2]; + + sub_v2_v2v2(tangent, mt->uv[(element->tfindex + 1)%nverts], mt->uv[element->tfindex]); + sub_v2_v2v2(internal, mt->uv[index], mt->uv[element->tfindex]); + + /* choose one of the normals */ + normal[0] = tangent[1]; + normal[1] = -tangent[0]; + + /* if normal points inside the face, invert */ + if(dot_v2v2(normal, internal) > 0){ + normal[0] = -tangent[1]; + normal[1] = tangent[0]; + } + + normalize_v2(normal); +} + +static int stitch_init(bContext *C, wmOperator *op) +{ + /* for fast edge lookup... */ + GHash *edgeHash; + /* ...and actual edge storage */ + UvEdge *edges; + int total_edges; + /* maps uvelements to their first coincident uv */ + int *map; + int counter = 0, i; + EditFace *efa; + EditMesh *em; + GHashIterator* ghi; + UvEdge *all_edges; + StitchState *state = MEM_mallocN(sizeof(StitchState), "stitch state"); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + + Object *obedit = CTX_data_edit_object(C); + + op->customdata = state; + + if(!state) + return 0; + + /* initialize state */ + state->use_limit = RNA_boolean_get(op->ptr, "use_limit"); + state->limit_dist = RNA_float_get(op->ptr, "limit"); + state->em = em = BKE_mesh_get_editmesh((Mesh*)obedit->data); + state->snap_islands = RNA_boolean_get(op->ptr, "snap_islands"); + state->static_island = RNA_int_get(op->ptr, "static_island"); + state->midpoints = RNA_boolean_get(op->ptr, "midpoint_snap"); + /* in uv synch selection, all uv's are visible */ + if(ts->uv_flag & UV_SYNC_SELECTION){ + state->element_map = EDBM_make_uv_element_map(state->em, 0, 1); + }else{ + state->element_map = EDBM_make_uv_element_map(state->em, 1, 1); + } + if(!state->element_map){ + stitch_state_delete(state); + return 0; + } + + /* Entirely possible if redoing last operator that static island is bigger than total number of islands. + * This ensures we get no hang in the island checking code in stitch_process_data. */ + state->static_island %= state->element_map->totalIslands; + + /* Count 'unique' uvs */ + for(i = 0; i < state->element_map->totalUVs; i++){ + if(state->element_map->buf[i].separate){ + counter++; + } + } + + /* Allocate the unique uv buffers */ + state->uvs = MEM_mallocN(sizeof(*state->uvs)*counter, "uv_stitch_unique_uvs"); + /* internal uvs need no normals but it is hard and slow to keep a map of + * normals only for boundary uvs, so allocating for all uvs */ + state->normals = MEM_callocN(sizeof(*state->normals)*counter*2, "uv_stitch_normals"); + state->total_separate_uvs = counter; + /* we can at most have totalUVs edges or uvs selected. Actually they are less, considering we store only + * unique uvs for processing but I am accounting for all bizarre cases, especially for edges, this way */ + state->selection_stack = MEM_mallocN(sizeof(*state->selection_stack)*counter, "uv_stitch_selection_stack"); + state->map = map = MEM_mallocN(sizeof(*map)*state->element_map->totalUVs, "uv_stitch_unique_map"); + /* Allocate the edge stack */ + edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash"); + all_edges = MEM_mallocN(sizeof(*all_edges)*state->element_map->totalUVs, "stitch_all_edges"); + + if(!state->selection_stack || !state->uvs || !map || !edgeHash || !all_edges){ + stitch_state_delete(state); + return 0; + } + + /* So that we can use this as index for the UvElements */ + counter = -1; + /* initialize the unique UVs and map */ + for(i = 0; i < state->em->totvert; i++){ + UvElement *element = state->element_map->vert[i]; + for(; element; element = element->next){ + if(element->separate){ + counter++; + state->uvs[counter] = element; + } + /* pointer arithmetic to the rescue, as always :)*/ + map[element - state->element_map->buf] = counter; + } + } + + /* Now, on to generate our uv connectivity data */ + for(efa = state->em->faces.first, counter = 0; efa; efa = efa->next){ + if((ts->uv_flag & UV_SYNC_SELECTION) || (!efa->h && efa->f & SELECT)){ + int nverts = efa->v4 ? 4 : 3; + + for(i = 0; i < nverts; i++){ + UvElement *element = ED_get_uv_element(state->element_map, efa, i); + int offset1, itmp1 = element - state->element_map->buf; + int offset2, itmp2 = ED_get_uv_element(state->element_map, efa, (i+1)%nverts) - state->element_map->buf; + + offset1 = map[itmp1]; + offset2 = map[itmp2]; + + all_edges[counter].flag = 0; + all_edges[counter].element = element; + /* using an order policy, sort uvs according to address space. This avoids + * Having two different UvEdges with the same uvs on different positions */ + if(offset1 < offset2){ + all_edges[counter].uv1 = offset1; + all_edges[counter].uv2 = offset2; + } + else{ + all_edges[counter].uv1 = offset2; + all_edges[counter].uv2 = offset1; + } + + if(BLI_ghash_haskey(edgeHash, &all_edges[counter])){ + char *flag = BLI_ghash_lookup(edgeHash, &all_edges[counter]); + *flag = 0; + } + else{ + BLI_ghash_insert(edgeHash, &all_edges[counter], &(all_edges[counter].flag)); + all_edges[counter].flag = STITCH_BOUNDARY; + } + counter++; + } + } + } + + + ghi = BLI_ghashIterator_new(edgeHash); + total_edges = 0; + /* fill the edges with data */ + for(i = 0; !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){ + UvEdge *edge = ((UvEdge *)BLI_ghashIterator_getKey(ghi)); + if(edge->flag & STITCH_BOUNDARY){ + total_edges++; + } + } + state->edges = edges = MEM_mallocN(sizeof(*edges)*total_edges, "stitch_edges"); + if(!ghi || !edges){ + MEM_freeN(all_edges); + stitch_state_delete(state); + return 0; + } + + state->total_boundary_edges = total_edges; + + /* fill the edges with data */ + for(i = 0, BLI_ghashIterator_init(ghi, edgeHash); !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){ + UvEdge *edge = ((UvEdge *)BLI_ghashIterator_getKey(ghi)); + if(edge->flag & STITCH_BOUNDARY){ + edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi)); + } + } + + /* cleanup temporary stuff */ + BLI_ghashIterator_free(ghi); + MEM_freeN(all_edges); + + /* refill hash with new pointers to cleanup duplicates */ + BLI_ghash_free(edgeHash, NULL, NULL); + + /***** calculate 2D normals for boundary uvs *****/ + + /* we use boundary edges to calculate 2D normals. + * to disambiguate the direction of the normal, we also need + * a point "inside" the island, that can be provided by + * the opposite uv for a quad, or the next uv for a triangle. */ + + for(i = 0; i < total_edges; i++){ + float normal[2]; + stitch_calculate_edge_normal(em, edges + i, normal); + + add_v2_v2(state->normals + edges[i].uv1*2, normal); + add_v2_v2(state->normals + edges[i].uv2*2, normal); + + normalize_v2(state->normals + edges[i].uv1*2); + normalize_v2(state->normals + edges[i].uv2*2); + } + + + /***** fill selection stack *******/ + + state->selection_size = 0; + + /* Load old selection if redoing operator with different settings */ + if(RNA_struct_property_is_set(op->ptr, "selection")){ + int faceIndex, elementIndex; + UvElement *element; + + EM_init_index_arrays(em, 0, 0, 1); + + + RNA_BEGIN(op->ptr, itemptr, "selection") { + faceIndex = RNA_int_get(&itemptr, "face_index"); + elementIndex = RNA_int_get(&itemptr, "element_index"); + efa = EM_get_face_for_index(faceIndex); + element = ED_get_uv_element(state->element_map, efa, elementIndex); + stitch_select_uv(element, state, 1); + } + RNA_END; + + EM_free_index_arrays(); + /* Clear the selection */ + RNA_collection_clear(op->ptr, "selection"); + + } else { + for(efa = state->em->faces.first ; efa; efa = efa->next){ + int numOfVerts; + MTFace *mt; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + numOfVerts = efa->v4 ? 4 : 3; + + for(i = 0; i < numOfVerts; i++){ + if(uvedit_uv_selected(scene, efa, mt, i)){ + UvElement *element = ED_get_uv_element(state->element_map, efa, i); + stitch_select_uv(element, state, 1); + } + } + } + } + + /***** initialise static island preview data *****/ + + state->quads_per_island = MEM_mallocN(sizeof(*state->quads_per_island)*state->element_map->totalIslands, + "stitch island quads"); + state->tris_per_island = MEM_mallocN(sizeof(*state->tris_per_island)*state->element_map->totalIslands, + "stitch island tris"); + for(i = 0; i < state->element_map->totalIslands; i++){ + state->quads_per_island[i] = 0; + state->tris_per_island[i] = 0; + } + + for(efa = state->em->faces.first; efa; efa = efa->next){ + UvElement *element = ED_get_uv_element(state->element_map, efa, 0); + + if(element){ + if(efa->v4){ + state->quads_per_island[element->island]++; + } + else { + state->tris_per_island[element->island]++; + } + } + } + + if(!stitch_process_data(state, scene, 0)){ + stitch_state_delete(state); + return 0; + } + + stitch_update_header(state, C); + return 1; +} + +static int stitch_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) +{ + Object *obedit = CTX_data_edit_object(C); + if(!stitch_init(C, op)) + return OPERATOR_CANCELLED; + + WM_event_add_modal_handler(C, op); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + return OPERATOR_RUNNING_MODAL; +} + +static void stitch_exit(bContext *C, wmOperator *op, int finished) +{ + StitchState *stitch_state; + Scene *scene; + SpaceImage *sima; + ScrArea *sa= CTX_wm_area(C); + Object *obedit; + + scene= CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + sima= CTX_wm_space_image(C); + + stitch_state = (StitchState *)op->customdata; + + if(finished){ + EditFace *efa; + int i; + + RNA_float_set(op->ptr, "limit", stitch_state->limit_dist); + RNA_boolean_set(op->ptr, "use_limit", stitch_state->use_limit); + RNA_boolean_set(op->ptr, "snap_islands", stitch_state->snap_islands); + RNA_int_set(op->ptr, "static_island", stitch_state->static_island); + RNA_boolean_set(op->ptr, "midpoint_snap", stitch_state->midpoints); + + for(i = 0, efa = stitch_state->em->faces.first; efa; efa = efa->next, i++){ + efa->tmp.l = i; + } + + /* Store selection for re-execution of stitch */ + for(i = 0; i < stitch_state->selection_size; i++){ + PointerRNA itemptr; + UvElement *element = stitch_state->selection_stack[i]; + + RNA_collection_add(op->ptr, "selection", &itemptr); + + RNA_int_set(&itemptr, "face_index", element->face->tmp.l); + RNA_int_set(&itemptr, "element_index", element->tfindex); + } + + + uvedit_live_unwrap_update(sima, scene, obedit); + } + + if(sa) + ED_area_headerprint(sa, NULL); + + DAG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + BKE_mesh_end_editmesh(obedit->data, stitch_state->em); + + stitch_state_delete(stitch_state); + op->customdata = NULL; + + stitch_preview_delete(); +} + + +static int stitch_cancel(bContext *C, wmOperator *op) +{ + stitch_exit(C, op, 0); + return OPERATOR_CANCELLED; +} + + +static int stitch_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + + if(!stitch_init(C, op)) + return OPERATOR_CANCELLED; + if(stitch_process_data((StitchState *)op->customdata, scene, 1)){ + stitch_exit(C, op, 1); + return OPERATOR_FINISHED; + }else { + return stitch_cancel(C, op); + } +} + +static void stitch_select(bContext *C, Scene *scene, wmEvent *event, StitchState *stitch_state){ + /* add uv under mouse to processed uv's */ + float co[2]; + NearestHit hit; + ARegion *ar= CTX_wm_region(C); + Image *ima= CTX_data_edit_image(C); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + uv_find_nearest_vert(scene, ima, stitch_state->em, co, NULL, &hit); + + if(hit.efa) + { + /* Add vertex to selection, deselect all common uv's of vert other + * than selected and update the preview. This behavior was decided so that + * you can do stuff like deselect the opposite stitchable vertex and the initial still gets deselected */ + + /* This works due to setting of tmp in find nearest uv vert */ + UvElement *element = ED_get_uv_element(stitch_state->element_map, hit.efa, hit.uv); + stitch_select_uv(element, stitch_state, 0); + + } +} + +static int stitch_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + StitchState *stitch_state; + Scene *scene = CTX_data_scene(C); + + stitch_state = (StitchState *)op->customdata; + + switch(event->type){ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Cancel */ + case ESCKEY: + return stitch_cancel(C, op); + + + case LEFTMOUSE: + if(event->shift && (U.flag & USER_LMOUSESELECT)){ + if(event->val == KM_RELEASE){ + stitch_select(C, scene, event, stitch_state); + + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + } + break; + } + case PADENTER: + case RETKEY: + if(stitch_process_data(stitch_state, scene, 1)){ + stitch_exit(C, op, 1); + return OPERATOR_FINISHED; + } + else { + return stitch_cancel(C, op); + } + + /* Increase limit */ + case PADPLUSKEY: + case WHEELUPMOUSE: + if(event->alt){ + stitch_state->limit_dist += 0.01; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + else{ + return OPERATOR_PASS_THROUGH; + } + /* Decrease limit */ + case PADMINUS: + case WHEELDOWNMOUSE: + if(event->alt){ + stitch_state->limit_dist -= 0.01; + stitch_state->limit_dist = MAX2(0.01, stitch_state->limit_dist); + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + }else{ + return OPERATOR_PASS_THROUGH; + } + + /* Use Limit (Default off)*/ + case LKEY: + if(event->val == KM_PRESS){ + stitch_state->use_limit = !stitch_state->use_limit; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + return OPERATOR_RUNNING_MODAL; + + case IKEY: + if(event->val == KM_PRESS){ + stitch_state->static_island++; + stitch_state->static_island %= stitch_state->element_map->totalIslands; + + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + return OPERATOR_RUNNING_MODAL; + + case MKEY: + if(event->val == KM_PRESS){ + stitch_state->midpoints = !stitch_state->midpoints; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + } + break; + + /* Select geometry*/ + case RIGHTMOUSE: + if(!event->shift){ + return stitch_cancel(C, op); + } + if(event->val == KM_RELEASE && !(U.flag & USER_LMOUSESELECT)){ + stitch_select(C, scene, event, stitch_state); + + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + return OPERATOR_RUNNING_MODAL; + + /* snap islands on/off */ + case SKEY: + if(event->val == KM_PRESS){ + stitch_state->snap_islands = !stitch_state->snap_islands; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } else + return OPERATOR_RUNNING_MODAL; + + default: + return OPERATOR_RUNNING_MODAL; + } + + /* if updated settings, renew feedback message */ + stitch_update_header(stitch_state, C); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_RUNNING_MODAL; +} + +void UV_OT_stitch(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Stitch"; + ot->description = "Stitch selected UV vertices by proximity"; + ot->idname = "UV_OT_stitch"; + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + /* api callbacks */ + ot->invoke = stitch_invoke; + ot->modal = stitch_modal; + ot->exec = stitch_exec; + ot->cancel = stitch_cancel; + ot->poll= ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "use_limit", 0, "Use Limit", "Stitch UVs within a specified limit distance"); + RNA_def_boolean(ot->srna, "snap_islands", 1, "Snap Islands", "Snap islands together. On edge stitch mode, rotates the islands too"); + + RNA_def_float(ot->srna, "limit", 0.01f, 0.0f, FLT_MAX, "Limit", "Limit distance in normalized coordinates", 0.0, FLT_MAX); + RNA_def_int(ot->srna, "static_island", 0, 0, INT_MAX, "Static Island", "Island that stays in place when stitching islands", 0, INT_MAX); + RNA_def_boolean(ot->srna, "midpoint_snap", 0, "Snap At Midpoint", "Uv's are stitched at midpoint instead of at static island"); + prop = RNA_def_collection_runtime(ot->srna, "selection", &RNA_SelectedUvElement, "Selection", ""); + /* Selection should not be editable or viewed in toolbar */ + RNA_def_property_flag(prop, PROP_HIDDEN); +} + +#endif /* BMESH TODO */ diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 3ed0cd02838..3377fdad9db 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -40,6 +40,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_modifier_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -50,12 +51,15 @@ #include "BLI_rand.h" #include "BLI_string.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_subsurf.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_depsgraph.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_report.h" #include "BKE_tessmesh.h" #include "BLI_math.h" @@ -318,6 +322,215 @@ static ParamHandle *construct_param_handle(Scene *scene, BMEditMesh *em, return handle; } +#if 0 /* BMESH_TODO */ +static void texface_from_original_index(EditFace *editFace, MTFace *texFace, int index, float **uv, ParamBool *pin, ParamBool *select, Scene *scene) +{ + int i, nverts = (editFace->v4)? 4: 3; + + *uv = NULL; + *pin = 0; + *select = 1; + + if(index == ORIGINDEX_NONE) + return; + + for(i = 0; i < nverts; i++) { + if((*(&editFace->v1 + i))->tmp.t == index) { + *uv = texFace->uv[i]; + *pin = ((texFace->unwrap & TF_PIN_MASK(i)) != 0); + *select = (uvedit_uv_selected(scene, editFace, texFace, i) != 0); + } + } +} + +/* unwrap handle initialization for subsurf aware-unwrapper. The many modifications required to make the original function(see above) + * work justified the existence of a new function. */ +static ParamHandle *construct_param_handle_subsurfed(Scene *scene, EditMesh *editMesh, short fill, short sel, short correct_aspect) +{ + ParamHandle *handle; + /* index pointers */ + MFace *face; + MEdge *edge; + EditVert *editVert; + MTFace *texface; + EditFace *editFace, **editFaceTmp; + EditEdge *editEdge, **editEdgeTmp; + int i; + + /* modifier initialization data, will control what type of subdivision will happen*/ + SubsurfModifierData smd = {{0}}; + /* Used to hold subsurfed Mesh */ + DerivedMesh *derivedMesh, *initialDerived; + /* holds original indices for subsurfed mesh */ + int *origVertIndices, *origFaceIndices, *origEdgeIndices; + /* Holds vertices of subsurfed mesh */ + MVert *subsurfedVerts; + MEdge *subsurfedEdges; + MFace *subsurfedFaces; + MTFace *subsurfedTexfaces; + /* number of vertices and faces for subsurfed mesh*/ + int numOfEdges, numOfFaces; + + /* holds a map to editfaces for every subsurfed MFace. These will be used to get hidden/ selected flags etc. */ + EditFace **faceMap; + /* Mini container to hold all EditFaces so that they may be indexed easily and fast. */ + EditFace **editFaceArray; + /* similar to the above, we need a way to map edges to their original ones */ + EditEdge **edgeMap; + EditEdge **editEdgeArray; + + handle = param_construct_begin(); + + if(correct_aspect) { + EditFace *eface = EM_get_actFace(editMesh, 1); + + if(eface) { + float aspx, aspy; + texface= CustomData_em_get(&editMesh->fdata, eface->data, CD_MTFACE); + + ED_image_uv_aspect(texface->tpage, &aspx, &aspy); + + if(aspx!=aspy) + param_aspect_ratio(handle, aspx, aspy); + } + } + + /* number of subdivisions to perform */ + smd.levels = scene->toolsettings->uv_subsurf_level; + smd.subdivType = ME_CC_SUBSURF; + + initialDerived = CDDM_from_editmesh(editMesh, NULL); + derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd, + 0, NULL, 0, 0, 1); + + initialDerived->release(initialDerived); + + /* get the derived data */ + subsurfedVerts = derivedMesh->getVertArray(derivedMesh); + subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh); + subsurfedFaces = derivedMesh->getFaceArray(derivedMesh); + + origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX); + origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX); + origFaceIndices = derivedMesh->getFaceDataArray(derivedMesh, CD_ORIGINDEX); + + subsurfedTexfaces = derivedMesh->getFaceDataArray(derivedMesh, CD_MTFACE); + + numOfEdges = derivedMesh->getNumEdges(derivedMesh); + numOfFaces = derivedMesh->getNumFaces(derivedMesh); + + faceMap = MEM_mallocN(numOfFaces*sizeof(EditFace *), "unwrap_edit_face_map"); + editFaceArray = MEM_mallocN(editMesh->totface*sizeof(EditFace *), "unwrap_editFaceArray"); + + /* fill edit face array with edit faces */ + for(editFace = editMesh->faces.first, editFaceTmp = editFaceArray; editFace; editFace= editFace->next, editFaceTmp++) + *editFaceTmp = editFace; + + /* map subsurfed faces to original editFaces */ + for(i = 0; i < numOfFaces; i++) + faceMap[i] = editFaceArray[origFaceIndices[i]]; + + MEM_freeN(editFaceArray); + + edgeMap = MEM_mallocN(numOfEdges*sizeof(EditEdge *), "unwrap_edit_edge_map"); + editEdgeArray = MEM_mallocN(editMesh->totedge*sizeof(EditEdge *), "unwrap_editEdgeArray"); + + /* fill edit edge array with edit edges */ + for(editEdge = editMesh->edges.first, editEdgeTmp = editEdgeArray; editEdge; editEdge= editEdge->next, editEdgeTmp++) + *editEdgeTmp = editEdge; + + /* map subsurfed edges to original editEdges */ + for(i = 0; i < numOfEdges; i++) { + /* not all edges correspond to an old edge */ + edgeMap[i] = (origEdgeIndices[i] != -1)? + editEdgeArray[origEdgeIndices[i]] : NULL; + } + + MEM_freeN(editEdgeArray); + + /* we need the editvert indices too */ + for(editVert = editMesh->verts.first, i=0; editVert; editVert = editVert->next, i++) + editVert->tmp.t = i; + + /* Prepare and feed faces to the solver */ + for(i = 0; i < numOfFaces; i++) { + ParamKey key, vkeys[4]; + ParamBool pin[4], select[4]; + float *co[4]; + float *uv[4]; + EditFace *origFace = faceMap[i]; + MTFace *origtexface = (MTFace *)CustomData_em_get(&editMesh->fdata, origFace->data, CD_MTFACE); + + face = subsurfedFaces+i; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(origFace->h) + continue; + } + else { + if((origFace->h) || (sel && (origFace->f & SELECT)==0)) + continue; + } + + /* Now we feed the rest of the data from the subsurfed faces */ + texface= subsurfedTexfaces+i; + + /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */ + key = (ParamKey)face; + vkeys[0] = (ParamKey)face->v1; + vkeys[1] = (ParamKey)face->v2; + vkeys[2] = (ParamKey)face->v3; + vkeys[3] = (ParamKey)face->v4; + + co[0] = subsurfedVerts[face->v1].co; + co[1] = subsurfedVerts[face->v2].co; + co[2] = subsurfedVerts[face->v3].co; + co[3] = subsurfedVerts[face->v4].co; + + /* This is where all the magic is done. If the vertex exists in the, we pass the original uv pointer to the solver, thus + * flushing the solution to the edit mesh. */ + texface_from_original_index(origFace, origtexface, origVertIndices[face->v1], &uv[0], &pin[0], &select[0], scene); + texface_from_original_index(origFace, origtexface, origVertIndices[face->v2], &uv[1], &pin[1], &select[1], scene); + texface_from_original_index(origFace, origtexface, origVertIndices[face->v3], &uv[2], &pin[2], &select[2], scene); + texface_from_original_index(origFace, origtexface, origVertIndices[face->v4], &uv[3], &pin[3], &select[3], scene); + + param_face_add(handle, key, 4, vkeys, co, uv, pin, select); + } + + /* these are calculated from original mesh too */ + for(edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) { + if((edgeMap[i] != NULL) && edgeMap[i]->seam) { + ParamKey vkeys[2]; + vkeys[0] = (ParamKey)edge->v1; + vkeys[1] = (ParamKey)edge->v2; + param_edge_set_seam(handle, vkeys); + } + } + + param_construct_end(handle, fill, 0); + + /* cleanup */ + MEM_freeN(faceMap); + MEM_freeN(edgeMap); + derivedMesh->release(derivedMesh); + + return handle; +} + +#else + +static ParamHandle *construct_param_handle_subsurfed(Scene *scene, BMEditMesh *editMesh, short fill, short sel, short correct_aspect) +{ + (void)scene; + (void)editMesh; + (void)fill; + (void)sel; + (void)correct_aspect; + return NULL; +} + +#endif /* BMESH_TODO */ + /* ******************** Minimize Stretch operator **************** */ typedef struct MinStretch { @@ -619,12 +832,16 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) BMEditMesh *em= ((Mesh*)obedit->data)->edit_btmesh; short abf = scene->toolsettings->unwrapper == 0; short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; + short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF; if(!ED_uvedit_test(obedit)) { return; } - liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1); + if(use_subsurf) + liveHandle = construct_param_handle_subsurfed(scene, em, fillholes, 0, 1); + else + liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1); param_lscm_begin(liveHandle, PARAM_TRUE, abf); } @@ -937,9 +1154,12 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) const short fill_holes= scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; const short correct_aspect= !(scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT); - short implicit= 0; + const short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF; - handle= construct_param_handle(scene, em, implicit, fill_holes, sel, correct_aspect); + if(use_subsurf) + handle = construct_param_handle_subsurfed(scene, em, fill_holes, sel, correct_aspect); + else + handle= construct_param_handle(scene, em, 0, fill_holes, sel, correct_aspect); param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0); param_lscm_solve(handle); @@ -960,6 +1180,9 @@ static int unwrap_exec(bContext *C, wmOperator *op) int method = RNA_enum_get(op->ptr, "method"); int fill_holes = RNA_boolean_get(op->ptr, "fill_holes"); int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); + int use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data"); + int subsurf_level = RNA_int_get(op->ptr, "uv_subsurf_level"); + float obsize[3], unitsize[3] = {1.0f, 1.0f, 1.0f}; short implicit= 0; if(!uvedit_have_selection(scene, em, implicit)) { @@ -971,8 +1194,14 @@ static int unwrap_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + mat4_to_size(obsize, obedit->obmat); + if(!compare_v3v3(obsize, unitsize, 1e-4f)) + BKE_report(op->reports, RPT_INFO, "Object scale is not 1.0. Unwrap will operate on a non-scaled version of the mesh."); + /* remember last method for live unwrap */ scene->toolsettings->unwrapper = method; + + scene->toolsettings->uv_subsurf_level = subsurf_level; if(fill_holes) scene->toolsettings->uvcalc_flag |= UVCALC_FILLHOLES; else scene->toolsettings->uvcalc_flag &= ~UVCALC_FILLHOLES; @@ -980,6 +1209,9 @@ static int unwrap_exec(bContext *C, wmOperator *op) if(correct_aspect) scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT; else scene->toolsettings->uvcalc_flag |= UVCALC_NO_ASPECT_CORRECT; + if(use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF; + else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF; + /* execute unwrap */ ED_unwrap_lscm(scene, obedit, TRUE); @@ -1013,6 +1245,8 @@ void UV_OT_unwrap(wmOperatorType *ot) "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry"); RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); + RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Data", "Map UV's taking vertex position after subsurf into account"); + RNA_def_int(ot->srna, "uv_subsurf_level", 1, 1, 6, "SubSurf Target", "Number of times to subdivide before calculating UV's", 1, 6); } /**************** Project From View operator **************/ diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 5d36ba169f3..59802f2cf58 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -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); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 2ac08d5e9f2..38fc513521b 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -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 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 2fa785e4156..44bf11795c1 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -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 */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 23e650bbf9e..60e53ce7ea2 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -421,6 +421,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; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0dbc0f63d4d..7c30bc21a69 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -65,6 +65,18 @@ #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"}, @@ -269,9 +281,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; @@ -1431,10 +1449,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"); @@ -3844,6 +3890,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; @@ -4177,6 +4245,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); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index cd929d3aeda..c3c84c66567 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -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); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 7b3c70aa896..24486c227b7 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -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"); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 24af3ffedc1..661bd90435c 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -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); diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 4f79121f38e..452c027ffa0 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -232,6 +232,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){} diff --git a/source/tests/bl_mesh_modifiers.py b/source/tests/bl_mesh_modifiers.py index 6a4e46a1832..789a4037659 100644 --- a/source/tests/bl_mesh_modifiers.py +++ b/source/tests/bl_mesh_modifiers.py @@ -31,11 +31,12 @@ import math USE_QUICK_RENDER = False - +IS_BMESH = hasattr(__import__("bpy").types, "LoopColors") # ----------------------------------------------------------------------------- # utility funcs + def render_gl(context, filepath, shade): def ctx_viewport_shade(context, shade): @@ -61,15 +62,16 @@ def render_gl(context, filepath, shade): ctx_viewport_shade(context, shade) + #~ # stop to inspect! + #~ if filepath == "test_cube_shell_solidify_subsurf_wp_wire": + #~ assert(0) + #~ else: + #~ return + bpy.ops.render.opengl(write_still=True, view_context=True) - # stop to inspect! - #~ if filepath == "test_cube_like_subsurf_single_wp_wire": - #~ assert(0) - - def render_gl_all_modes(context, obj, filepath=""): assert(obj != None) @@ -201,8 +203,12 @@ def defaults_object(obj): mesh.show_normal_vertex = True # lame! - for face in mesh.faces: - face.use_smooth = True + if IS_BMESH: + for poly in mesh.polygons: + poly.use_smooth = True + else: + for face in mesh.faces: + face.use_smooth = True def defaults_modifier(mod): @@ -213,6 +219,18 @@ def defaults_modifier(mod): # ----------------------------------------------------------------------------- # models (utils) + +if IS_BMESH: + def mesh_bmesh_poly_elems(poly, elems): + vert_start = poly.loop_start + vert_total = poly.loop_total + return elems[vert_start:vert_start + vert_total] + + def mesh_bmesh_poly_vertices(poly): + return [loop.vertex_index + for loop in mesh_bmesh_poly_elems(poly, poly.id_data.loops)] + + def mesh_bounds(mesh): xmin = ymin = zmin = +100000000.0 xmax = ymax = zmax = -100000000.0 @@ -231,23 +249,67 @@ def mesh_bounds(mesh): def mesh_uv_add(obj): + + uvs = ((0.0, 0.0), + (0.0, 1.0), + (1.0, 1.0), + (1.0, 0.0)) + uv_lay = obj.data.uv_textures.new() - for uv in uv_lay.data: - uv.uv1 = 0.0, 0.0 - uv.uv2 = 0.0, 1.0 - uv.uv3 = 1.0, 1.0 - uv.uv4 = 1.0, 0.0 + + if IS_BMESH: + # XXX, odd that we need to do this. until uvs and texface + # are separated we will need to keep it + uv_loops = obj.data.uv_loop_layers[-1] + uv_list = uv_loops.data[:] + for poly in obj.data.polygons: + poly_uvs = mesh_bmesh_poly_elems(poly, uv_list) + for i, c in enumerate(poly_uvs): + c.uv = uvs[i % 4] + else: + for uv in uv_lay.data: + uv.uv1 = uvs[0] + uv.uv2 = uvs[1] + uv.uv3 = uvs[2] + uv.uv4 = uvs[3] return uv_lay def mesh_vcol_add(obj, mode=0): + + colors = ((0.0, 0.0, 0.0), # black + (1.0, 0.0, 0.0), # red + (0.0, 1.0, 0.0), # green + (0.0, 0.0, 1.0), # blue + (1.0, 1.0, 0.0), # yellow + (0.0, 1.0, 1.0), # cyan + (1.0, 0.0, 1.0), # magenta + (1.0, 1.0, 1.0), # white + ) + + def colors_get(i): + return colors[i % len(colors)] + vcol_lay = obj.data.vertex_colors.new() - for col in vcol_lay.data: - col.color1 = 1.0, 0.0, 0.0 - col.color2 = 0.0, 1.0, 0.0 - col.color3 = 0.0, 0.0, 1.0 - col.color4 = 0.0, 0.0, 0.0 + + mesh = obj.data + + if IS_BMESH: + col_list = vcol_lay.data[:] + for poly in mesh.polygons: + face_verts = mesh_bmesh_poly_vertices(poly) + poly_cols = mesh_bmesh_poly_elems(poly, col_list) + for i, c in enumerate(poly_cols): + c.color = colors[i % 4] + else: + for i, col in enumerate(vcol_lay.data): + face_verts = mesh.faces[i].vertices + col.color1 = colors_get(face_verts[0]) + col.color2 = colors_get(face_verts[1]) + col.color3 = colors_get(face_verts[2]) + if len(face_verts) == 4: + col.color4 = colors_get(face_verts[3]) return vcol_lay @@ -364,13 +426,19 @@ def modifier_hook_add(scene, obj, use_vgroup=True): # no nice way to add hooks from py api yet # assume object mode, hook first face! mesh = obj.data - + if use_vgroup: for v in mesh.vertices: v.select = True else: for v in mesh.vertices: v.select = False + + if IS_BMESH: + face_verts = mesh_bmesh_poly_vertices(mesh.polygons[0]) + else: + face_verts = mesh.faces[0].vertices[:] + for i in mesh.faces[0].vertices: mesh.vertices[i].select = True @@ -406,7 +474,11 @@ def modifier_build_add(scene, obj): defaults_modifier(mod) # ensure we display some faces - totface = len(obj.data.faces) + if IS_BMESH: + totface = len(obj.data.polygons) + else: + totface = len(obj.data.faces) + mod.frame_start = totface // 2 mod.frame_duration = totface |