diff --git a/release/scripts/addons/add_advanced_objects_menu/__init__.py b/release/scripts/addons/add_advanced_objects_menu/__init__.py index 46a3dffc7e5e..42a334452257 100644 --- a/release/scripts/addons/add_advanced_objects_menu/__init__.py +++ b/release/scripts/addons/add_advanced_objects_menu/__init__.py @@ -25,7 +25,7 @@ bl_info = { "name": "Add Advanced Objects", "author": "Meta Androcto", - "version": (0, 1, 5), + "version": (0, 1, 6), "blender": (2, 78, 0), "location": "View3D > Add ", "description": "Add Object & Camera extras", @@ -55,7 +55,6 @@ importlib.reload(arrange_on_curve) importlib.reload(mesh_easylattice) - else: from . import add_light_template from . import scene_objects_bi @@ -78,18 +77,18 @@ import bpy from bpy.types import ( - Menu, - AddonPreferences, - PropertyGroup, - ) + AddonPreferences, + Menu, + PropertyGroup, +) from bpy.props import ( - BoolProperty, - EnumProperty, - FloatProperty, - IntProperty, - StringProperty, - PointerProperty, - ) + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + StringProperty, + PointerProperty, +) # Define the "Scenes" menu @@ -204,15 +203,15 @@ class AdvancedObjPreferences(AddonPreferences): bl_idname = __name__ show_menu_list = BoolProperty( - name="Menu List", - description="Show/Hide the Add Menu items", - default=False - ) + name="Menu List", + description="Show/Hide the Add Menu items", + default=False + ) show_panel_list = BoolProperty( - name="Panels List", - description="Show/Hide the Panel items", - default=False - ) + name="Panels List", + description="Show/Hide the Panel items", + default=False + ) def draw(self, context): layout = self.layout @@ -315,214 +314,214 @@ class AdvancedObjProperties(PropertyGroup): # cubester # main properties cubester_check_audio = BoolProperty( - name="", - default=False - ) + name="", + default=False + ) cubester_audio_image = EnumProperty( - name="Input Type", - items=(("image", "Image", - "Use an Image as input for generating Geometry", "IMAGE_COL", 0), - ("audio", "Audio", - "Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1)) - ) + name="Input Type", + items=(("image", "Image", + "Use an Image as input for generating Geometry", "IMAGE_COL", 0), + ("audio", "Audio", + "Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1)) + ) cubester_audio_file_length = IntProperty( - default=0 - ) + default=0 + ) # audio cubester_audio_path = StringProperty( - default="", - name="Audio File", - subtype="FILE_PATH", - update=find_audio_length - ) + default="", + name="Audio File", + subtype="FILE_PATH", + update=find_audio_length + ) cubester_audio_min_freq = IntProperty( - name="Minimum Frequency", - min=20, max=100000, - default=20 - ) + name="Minimum Frequency", + min=20, max=100000, + default=20 + ) cubester_audio_max_freq = IntProperty( - name="Maximum Frequency", - min=21, max=999999, - default=5000 - ) + name="Maximum Frequency", + min=21, max=999999, + default=5000 + ) cubester_audio_offset_type = EnumProperty( - name="Offset Type", - items=(("freq", "Frequency Offset", ""), - ("frame", "Frame Offset", "")), - description="Type of offset per row of mesh" - ) + name="Offset Type", + items=(("freq", "Frequency Offset", ""), + ("frame", "Frame Offset", "")), + description="Type of offset per row of mesh" + ) cubester_audio_frame_offset = IntProperty( - name="Frame Offset", - min=0, max=10, - default=2 - ) + name="Frame Offset", + min=0, max=10, + default=2 + ) cubester_audio_block_layout = EnumProperty( - name="Block Layout", - items=(("rectangle", "Rectangular", ""), - ("radial", "Radial", "")) - ) + name="Block Layout", + items=(("rectangle", "Rectangular", ""), + ("radial", "Radial", "")) + ) cubester_audio_width_blocks = IntProperty( - name="Width Block Count", - min=1, max=10000, - default=5 - ) + name="Width Block Count", + min=1, max=10000, + default=5 + ) cubester_audio_length_blocks = IntProperty( - name="Length Block Count", - min=1, max=10000, - default=50 - ) + name="Length Block Count", + min=1, max=10000, + default=50 + ) # image cubester_load_type = EnumProperty( - name="Image Input Type", - items=(("single", "Single Image", ""), - ("multiple", "Image Sequence", "")) - ) + name="Image Input Type", + items=(("single", "Single Image", ""), + ("multiple", "Image Sequence", "")) + ) cubester_image = StringProperty( - default="", - name="" - ) + default="", + name="" + ) cubester_load_image = StringProperty( - default="", - name="Load Image", - subtype="FILE_PATH", - update=adjust_selected_image - ) + default="", + name="Load Image", + subtype="FILE_PATH", + update=adjust_selected_image + ) cubester_skip_images = IntProperty( - name="Image Step", - min=1, max=30, - default=1, - description="Step from image to image by this number" - ) + name="Image Step", + min=1, max=30, + default=1, + description="Step from image to image by this number" + ) cubester_max_images = IntProperty( - name="Max Number Of Images", - min=2, max=1000, - default=10, - description="Maximum number of images to be used" - ) + name="Max Number Of Images", + min=2, max=1000, + default=10, + description="Maximum number of images to be used" + ) cubester_frame_step = IntProperty( - name="Frame Step Size", - min=1, max=10, - default=4, - description="The number of frames each picture is used" - ) + name="Frame Step Size", + min=1, max=10, + default=4, + description="The number of frames each picture is used" + ) cubester_skip_pixels = IntProperty( - name="Skip # Pixels", - min=0, max=256, - default=64, - description="Skip this number of pixels before placing the next" - ) + name="Skip # Pixels", + min=0, max=256, + default=64, + description="Skip this number of pixels before placing the next" + ) cubester_mesh_style = EnumProperty( - name="Mesh Type", - items=(("blocks", "Blocks", ""), - ("plane", "Plane", "")), - description="Compose mesh of multiple blocks or of a single plane" - ) + name="Mesh Type", + items=(("blocks", "Blocks", ""), + ("plane", "Plane", "")), + description="Compose mesh of multiple blocks or of a single plane" + ) cubester_block_style = EnumProperty( - name="Block Style", - items=(("size", "Vary Size", ""), - ("position", "Vary Position", "")), - description="Vary Z-size of block, or vary Z-position" - ) + name="Block Style", + items=(("size", "Vary Size", ""), + ("position", "Vary Position", "")), + description="Vary Z-size of block, or vary Z-position" + ) cubester_height_scale = FloatProperty( - name="Height Scale", - subtype="DISTANCE", - min=0.1, max=2, - default=0.2 - ) + name="Height Scale", + subtype="DISTANCE", + min=0.1, max=2, + default=0.2 + ) cubester_invert = BoolProperty( - name="Invert Height", - default=False - ) + name="Invert Height", + default=False + ) # general adjustments cubester_size_per_hundred_pixels = FloatProperty( - name="Size Per 100 Blocks/Points", - subtype="DISTANCE", - min=0.001, max=5, - default=1 - ) + name="Size Per 100 Blocks/Points", + subtype="DISTANCE", + min=0.001, max=5, + default=1 + ) # material based stuff cubester_materials = EnumProperty( - name="Material", - items=(("vertex", "Vertex Colors", ""), - ("image", "Image", "")), - description="Color with vertex colors, or uv unwrap and use an image" - ) + name="Material", + items=(("vertex", "Vertex Colors", ""), + ("image", "Image", "")), + description="Color with vertex colors, or uv unwrap and use an image" + ) cubester_use_image_color = BoolProperty( - name="Use Original Image Colors'?", - default=True, - description="Use original image colors, or replace with an another one" - ) + name="Use Original Image Colors'?", + default=True, + description="Use original image colors, or replace with an another one" + ) cubester_color_image = StringProperty( - default="", - name="" - ) + default="", + name="" + ) cubester_load_color_image = StringProperty( - default="", - name="Load Color Image", - subtype="FILE_PATH", - update=adjust_selected_color_image - ) + default="", + name="Load Color Image", + subtype="FILE_PATH", + update=adjust_selected_color_image + ) cubester_vertex_colors = {} # advanced cubester_advanced = BoolProperty( - name="Advanced Options", - default=False - ) + name="Advanced Options", + default=False + ) cubester_random_weights = BoolProperty( - name="Random Weights", - default=False - ) + name="Random Weights", + default=False + ) cubester_weight_r = FloatProperty( - name="Red", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) + name="Red", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) cubester_weight_g = FloatProperty( - name="Green", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) + name="Green", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) cubester_weight_b = FloatProperty( - name="Blue", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) + name="Blue", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) cubester_weight_a = FloatProperty( - name="Alpha", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) + name="Alpha", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) # arrange_on_curve arrange_c_use_selected = BoolProperty( - name="Use Selected", - description="Use the selected objects to duplicate", - default=True, - ) + name="Use Selected", + description="Use the selected objects to duplicate", + default=True, + ) arrange_c_obj_arranjar = StringProperty( - name="" - ) + name="" + ) arrange_c_select_type = EnumProperty( - name="Type", - description="Select object or group", - items=[ - ('O', "Object", "Make duplicates of a specific object"), - ('G', "Group", "Make duplicates of the objects in a group"), - ], - default='O', - ) + name="Type", + description="Select object or group", + items=[ + ('O', "Object", "Make duplicates of a specific object"), + ('G', "Group", "Make duplicates of the objects in a group"), + ], + default='O', + ) def register(): bpy.utils.register_module(__name__) bpy.types.Scene.advanced_objects = PointerProperty( - type=AdvancedObjProperties - ) + type=AdvancedObjProperties + ) # Add "Extras" menu to the "Add" menu bpy.types.INFO_MT_add.append(menu) @@ -543,10 +542,6 @@ def unregister(): bpy.utils.unregister_module(__name__) del bpy.types.Scene.advanced_objects - # cleanup Easy Lattice Scene Property if it was created - if hasattr(bpy.types.Scene, "activelatticeobject"): - del bpy.types.Scene.activelatticeobject - if __name__ == "__main__": register() diff --git a/release/scripts/addons/add_advanced_objects_menu/arrange_on_curve.py b/release/scripts/addons/add_advanced_objects_menu/arrange_on_curve.py index b811263b151e..c292e341da92 100644 --- a/release/scripts/addons/add_advanced_objects_menu/arrange_on_curve.py +++ b/release/scripts/addons/add_advanced_objects_menu/arrange_on_curve.py @@ -31,6 +31,7 @@ class PanelDupliCurve(Panel): + bl_idname = "VIEW3D_PT_arranjar_numa_curva" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" bl_context = "objectmode" diff --git a/release/scripts/addons/add_advanced_objects_menu/cubester.py b/release/scripts/addons/add_advanced_objects_menu/cubester.py index 1a516bd07f65..87322f4feb84 100644 --- a/release/scripts/addons/add_advanced_objects_menu/cubester.py +++ b/release/scripts/addons/add_advanced_objects_menu/cubester.py @@ -25,28 +25,28 @@ bl_info = { "name": "CubeSter", "author": "Jacob Morris", - "version": (0, 7, 1), + "version": (0, 7, 2), "blender": (2, 78, 0), "location": "View 3D > Toolbar > CubeSter", "description": "Takes image, image sequence, or audio file and converts it " "into a height map based on pixel color and alpha values", "category": "Add Mesh" - } +} import bpy import bmesh from bpy.types import ( - Operator, - Panel, - ) + Operator, + Panel, +) import timeit from random import uniform from math import radians from os import ( - path, - listdir, - ) + path, + listdir, +) # create block at center position x, y with block width 2 * hx and 2 * hy and height of h @@ -146,18 +146,18 @@ def create_material(scene, ob, name): if adv_obj.cubester_materials == "image": mat.node_tree.links.new( - nodes["Image Texture"].outputs[0], - nodes["Diffuse BSDF"].inputs[0] - ) + nodes["Image Texture"].outputs[0], + nodes["Diffuse BSDF"].inputs[0] + ) mat.node_tree.links.new( - nodes["Texture Coordinate"].outputs[2], - nodes["Image Texture"].inputs[0] - ) + nodes["Texture Coordinate"].outputs[2], + nodes["Image Texture"].inputs[0] + ) else: mat.node_tree.links.new( - nodes["Attribute"].outputs[0], - nodes["Diffuse BSDF"].inputs[0] - ) + nodes["Attribute"].outputs[0], + nodes["Diffuse BSDF"].inputs[0] + ) else: if adv_obj.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER": tex = bpy.data.textures.new("CubeSter_" + name, "IMAGE") @@ -177,9 +177,11 @@ def create_mesh_from_audio(self, scene, verts, faces): audio_filepath = adv_obj.cubester_audio_path width = adv_obj.cubester_audio_width_blocks length = adv_obj.cubester_audio_length_blocks - size_per_hundred = adv_obj.cubester_size_per_hundred_pixels + size_per_hundred = adv_obj.cubester_size_per_hundred_pixels size = size_per_hundred / 100 + # Note: used for compatibility with vertex colors changes + bl_version = bool(bpy.app.version >= (2, 79, 1)) # create all blocks y = -(width / 2) * size + (size / 2) @@ -212,7 +214,8 @@ def create_mesh_from_audio(self, scene, verts, faces): # go through each column, step by appropriate amount for column in range(0, picture.size[0] * 4, 4 + skip_x * 4): r, g, b, a = get_pixel_values(picture, pixels, row, column) - vert_colors += [(r, g, b) for i in range(24)] + get_colors = (r, g, b, a) if bl_version else (r, g, b) + vert_colors += [get_colors for i in range(24)] bpy.ops.mesh.vertex_color_add() @@ -244,7 +247,8 @@ def create_mesh_from_audio(self, scene, verts, faces): for row in range(0, picture.size[1], skip_y + 1): for column in range(0, picture.size[0] * 4, 4 + skip_x * 4): r, g, b, a = get_pixel_values(picture, pixels, row, column) - frame_colors += [(r, g, b) for i in range(24)] + get_colors = (r, g, b, a) if bl_version else (r, g, b) + frame_colors += [get_colors for i in range(24)] frames_vert_colors.append(frame_colors) @@ -354,6 +358,8 @@ def create_mesh_from_image(self, scene, verts, faces): adv_obj = scene.advanced_objects picture = bpy.data.images[adv_obj.cubester_image] pixels = list(picture.pixels) + # Note: used for compatibility with vertex colors changes + bl_version = bool(bpy.app.version >= (2, 79, 1)) x_pixels = picture.size[0] / (adv_obj.cubester_skip_pixels + 1) y_pixels = picture.size[1] / (adv_obj.cubester_skip_pixels + 1) @@ -367,7 +373,6 @@ def create_mesh_from_image(self, scene, verts, faces): y = -height / 2 + half_width vert_colors = [] - weights = [uniform(0.0, 1.0) for i in range(4)] # random weights rows = 0 # go through each row of pixels stepping by adv_obj.cubester_skip_pixels + 1 @@ -377,16 +382,17 @@ def create_mesh_from_image(self, scene, verts, faces): # go through each column, step by appropriate amount for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4): r, g, b, a = get_pixel_values(picture, pixels, row, column) + get_colors = (r, g, b, a) if bl_version else (r, g, b) h = find_point_height(r, g, b, a, scene) # if not transparent if h != -1: if adv_obj.cubester_mesh_style == "blocks": create_block(x, y, half_width, h, verts, faces) - vert_colors += [(r, g, b) for i in range(24)] + vert_colors += [get_colors for i in range(24)] else: verts += [(x, y, h)] - vert_colors += [(r, g, b) for i in range(4)] + vert_colors += [get_colors for i in range(4)] x += step y += step @@ -464,14 +470,15 @@ def create_mesh_from_image(self, scene, verts, faces): for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1): for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4): r, g, b, a = get_pixel_values(picture, pixels, row, column) + get_colors = (r, g, b, a) if bl_version else (r, g, b) h = find_point_height(r, g, b, a, scene) if h != -1: frame_heights.append(h) if adv_obj.cubester_mesh_style == "blocks": - frame_colors += [(r, g, b) for i in range(24)] + frame_colors += [get_colors for i in range(24)] else: - frame_colors += [(r, g, b) for i in range(4)] + frame_colors += [get_colors for i in range(4)] if adv_obj.cubester_mesh_style == "plane": del vert_colors[len(vert_colors) - 4:len(vert_colors)] @@ -482,24 +489,24 @@ def create_mesh_from_image(self, scene, verts, faces): # determine what data to use if adv_obj.cubester_materials == "vertex" or scene.render.engine == "BLENDER_ENGINE": adv_obj.cubester_vertex_colors[ob.name] = { - "type": "vertex", "frames": frames_vert_colors, - "frame_skip": adv_obj.cubester_frame_step, - "total_images": max_images - } + "type": "vertex", "frames": frames_vert_colors, + "frame_skip": adv_obj.cubester_frame_step, + "total_images": max_images + } else: adv_obj.cubester_vertex_colors[ob.name] = { - "type": "image", "frame_skip": scene.cubester_frame_step, - "total_images": max_images - } + "type": "image", "frame_skip": adv_obj.cubester_frame_step, + "total_images": max_images + } att = get_image_node(ob.data.materials[0]) att.image_user.frame_duration = len(frames) * adv_obj.cubester_frame_step # animate mesh create_f_curves( - mesh, frames, - adv_obj.cubester_frame_step, - adv_obj.cubester_mesh_style - ) + mesh, frames, + adv_obj.cubester_frame_step, + adv_obj.cubester_mesh_style + ) # generate uv map for object @@ -695,7 +702,7 @@ def material_frame_handler(scene): class CubeSterPanel(Panel): - bl_idname = "OBJECT_PT.cubester" + bl_idname = "OBJECT_PT_cubester" bl_label = "CubeSter" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" @@ -879,11 +886,12 @@ def draw(self, context): class CubeSter(Operator): bl_idname = "mesh.cubester" - bl_label = "Generate Mesh" + bl_label = "Generate CubeSter Mesh" bl_description = "Generate a mesh from an Image or Sound File" bl_options = {"REGISTER", "UNDO"} def execute(self, context): + verts, faces = [], [] start = timeit.default_timer() @@ -901,7 +909,8 @@ def execute(self, context): return {"CANCELLED"} else: if (adv_obj.cubester_audio_path != "" and - path.isfile(adv_obj.cubester_audio_path) and adv_obj.cubester_check_audio is True): + path.isfile(adv_obj.cubester_audio_path) and + adv_obj.cubester_check_audio is True): create_mesh_from_audio(self, scene, verts, faces) created = adv_obj.cubester_audio_file_length @@ -913,19 +922,21 @@ def execute(self, context): stop = timeit.default_timer() if adv_obj.cubester_mesh_style == "blocks" or adv_obj.cubester_audio_image == "audio": - self.report({"INFO"}, - "CubeSter: {} blocks and {} frame(s) " - "in {}s".format(str(int(len(verts) / 8)), - str(created), - str(round(stop - start, 4))) - ) + self.report( + {"INFO"}, + "CubeSter: {} blocks and {} frame(s) " + "in {}s".format(str(int(len(verts) / 8)), + str(created), + str(round(stop - start, 4))) + ) else: - self.report({"INFO"}, - "CubeSter: {} points and {} frame(s) " - "in {}s" .format(str(len(verts)), - str(created), - str(round(stop - start, 4))) - ) + self.report( + {"INFO"}, + "CubeSter: {} points and {} frame(s) " + "in {}s" .format(str(len(verts)), + str(created), + str(round(stop - start, 4))) + ) return {"FINISHED"} diff --git a/release/scripts/addons/add_curve_extra_objects/__init__.py b/release/scripts/addons/add_curve_extra_objects/__init__.py index ae8cdf894fb8..42ae2d449a4c 100644 --- a/release/scripts/addons/add_curve_extra_objects/__init__.py +++ b/release/scripts/addons/add_curve_extra_objects/__init__.py @@ -225,9 +225,9 @@ def draw(self, context): icon="LAYER_USED") -class INFO_MT_curve_knots_add1(Menu): +class INFO_MT_curve_knots_add(Menu): # Define the "Extras" menu - bl_idname = "curve_knots_add" + bl_idname = "INFO_MT_curve_knots_add" bl_label = "Plants" def draw(self, context): @@ -253,7 +253,7 @@ def menu_func(self, context): icon='CURVE_DATA') layout.separator() - layout.menu("curve_knots_add", text="Knots", icon='CURVE_DATA') + layout.menu(INFO_MT_curve_knots_add.bl_idname, text="Knots", icon='CURVE_DATA') layout.separator() layout.operator("curve.curlycurve", text="Curly Curve", icon='CURVE_DATA') diff --git a/release/scripts/addons/add_curve_extra_objects/add_curve_simple.py b/release/scripts/addons/add_curve_extra_objects/add_curve_simple.py index 3e85fa18fca5..c6908ce9dbc0 100644 --- a/release/scripts/addons/add_curve_extra_objects/add_curve_simple.py +++ b/release/scripts/addons/add_curve_extra_objects/add_curve_simple.py @@ -1399,6 +1399,7 @@ def invoke(self, context, event): # Simple change panel class SimplePanel(Panel): + bl_idname = "VIEW3D_PT_simple_curve" bl_label = "Simple Curve" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" diff --git a/release/scripts/addons/add_curve_extra_objects/add_curve_spirofit_bouncespline.py b/release/scripts/addons/add_curve_extra_objects/add_curve_spirofit_bouncespline.py index 33caf120acdc..d0c1c747a3d9 100644 --- a/release/scripts/addons/add_curve_extra_objects/add_curve_spirofit_bouncespline.py +++ b/release/scripts/addons/add_curve_extra_objects/add_curve_spirofit_bouncespline.py @@ -979,6 +979,7 @@ def draw_spline_settings(self): # Tools Panel > Create # ------------------------------------------------------------ class SplinePanel(Panel): + bl_idname = "VIEW3D_PT_spirofit_spline" bl_space_type = "VIEW_3D" bl_context = "objectmode" bl_region_type = "TOOLS" diff --git a/release/scripts/addons/add_curve_sapling/__init__.py b/release/scripts/addons/add_curve_sapling/__init__.py index 48b45d312df6..274c1fefefea 100644 --- a/release/scripts/addons/add_curve_sapling/__init__.py +++ b/release/scripts/addons/add_curve_sapling/__init__.py @@ -235,7 +235,7 @@ def execute(self, context): class PresetMenu(Menu): """Create the preset menu by finding all preset files in the preset directory""" - bl_idname = "sapling.presetmenu" + bl_idname = "SAPLING_MT_preset" bl_label = "Presets" def draw(self, context): @@ -930,7 +930,7 @@ def draw(self, context): row.label(" ") row.prop(self, 'overwrite') row = box.row() - row.menu('sapling.presetmenu', text='Load Preset') + row.menu('SAPLING_MT_preset', text='Load Preset') row.prop(self, 'limitImport') elif self.chooseSet == '1': diff --git a/release/scripts/addons/add_mesh_extra_objects/mesh_discombobulator.py b/release/scripts/addons/add_mesh_extra_objects/mesh_discombobulator.py index 9d9469d29402..b677fc6cc5de 100644 --- a/release/scripts/addons/add_mesh_extra_objects/mesh_discombobulator.py +++ b/release/scripts/addons/add_mesh_extra_objects/mesh_discombobulator.py @@ -627,7 +627,7 @@ def execute(self, context): class discombobulator_dodads_list(Menu): - bl_idname = "object.discombobulator_dodad_list" + bl_idname = "OBJECT_MT_discombobulator_dodad_list" bl_label = "List of saved Doodads" bl_description = "List of the saved Doodad Object Names" bl_options = {'REGISTER'} @@ -644,7 +644,7 @@ def draw(self, context): class discombob_help(Menu): - bl_idname = "help.discombobulator" + bl_idname = "HELP_MT_discombobulator" bl_label = "Usage Information" bl_description = "Help" bl_options = {'REGISTER'} @@ -688,7 +688,7 @@ def draw(self, context): layout = self.layout row = layout.row() - row.menu('help.discombobulator', icon='INFO') + row.menu('HELP_MT_discombobulator', icon='INFO') box = layout.box() box.label("Protusions settings") row = box.row() @@ -740,7 +740,7 @@ def draw(self, context): doodle = len(bpy.context.scene.discomb.DISC_doodads) col.enabled = (True if doodle > 0 else False) - col.menu("object.discombobulator_dodad_list", + col.menu("OBJECT_MT_discombobulator_dodad_list", text="List of saved Doodads ({})".format(doodle)) box = layout.box() diff --git a/release/scripts/addons/ant_landscape/__init__.py b/release/scripts/addons/ant_landscape/__init__.py index e4ae01d18195..ba6e8b6f88e3 100644 --- a/release/scripts/addons/ant_landscape/__init__.py +++ b/release/scripts/addons/ant_landscape/__init__.py @@ -76,6 +76,7 @@ def menu_func_landscape(self, context): # Landscape Add Panel class panel_func_add_landscape(bpy.types.Panel): + bl_idname = "ANTLANDSCAPE_PT_add" bl_space_type = "VIEW_3D" bl_context = "objectmode" bl_region_type = "TOOLS" @@ -90,6 +91,7 @@ def draw(self, context): # Landscape Tools: class AntLandscapeToolsPanel(bpy.types.Panel): + bl_idname = "ANTLANDSCAPE_PT_tools" bl_space_type = "VIEW_3D" bl_context = "objectmode" bl_region_type = "TOOLS" diff --git a/release/scripts/addons/ant_landscape/add_mesh_ant_landscape.py b/release/scripts/addons/ant_landscape/add_mesh_ant_landscape.py index 5d25cf2404d3..6a36c42fd4e9 100644 --- a/release/scripts/addons/ant_landscape/add_mesh_ant_landscape.py +++ b/release/scripts/addons/ant_landscape/add_mesh_ant_landscape.py @@ -586,6 +586,14 @@ class AntAddLandscape(bpy.types.Operator): description="Automatic refresh" ) + @classmethod + def poll(self, context): + ob = context.object + if ob is not None: + if ob.mode == 'EDIT': + return False + return True + def draw(self, context): draw_ant_refresh(self, context) draw_ant_main(self, context, generate=True) diff --git a/release/scripts/addons/ant_landscape/ant_functions.py b/release/scripts/addons/ant_landscape/ant_functions.py index 3b515933282d..3c0f2c34f3cf 100644 --- a/release/scripts/addons/ant_landscape/ant_functions.py +++ b/release/scripts/addons/ant_landscape/ant_functions.py @@ -193,7 +193,10 @@ class AntLandscapeRegenerate(bpy.types.Operator): @classmethod def poll(cls, context): - return bpy.context.active_object.ant_landscape + ob = bpy.context.active_object + if ob.mode == 'EDIT': + return False + return ob.ant_landscape def execute(self, context): diff --git a/release/scripts/addons/archimesh/__init__.py b/release/scripts/addons/archimesh/__init__.py index e9ba3883aa33..cb22e1572e55 100644 --- a/release/scripts/addons/archimesh/__init__.py +++ b/release/scripts/addons/archimesh/__init__.py @@ -58,7 +58,7 @@ importlib.reload(achm_venetian_maker) importlib.reload(achm_main_panel) importlib.reload(achm_window_panel) - print("archimesh: Reloaded multifiles") + # print("archimesh: Reloaded multifiles") else: from . import achm_books_maker from . import achm_column_maker @@ -75,7 +75,7 @@ from . import achm_window_maker from . import achm_window_panel - print("archimesh: Imported multifiles") + # print("archimesh: Imported multifiles") # noinspection PyUnresolvedReferences import bpy diff --git a/release/scripts/addons/archimesh/achm_main_panel.py b/release/scripts/addons/archimesh/achm_main_panel.py index 288922f3f067..d12c2a6ba666 100644 --- a/release/scripts/addons/archimesh/achm_main_panel.py +++ b/release/scripts/addons/archimesh/achm_main_panel.py @@ -388,7 +388,7 @@ def execute(self, context): # Define panel class for main functions. # ------------------------------------------------------------------ class ArchimeshMainPanel(Panel): - bl_idname = "archimesh_main_panel" + bl_idname = "ARCHIMESH_PT_main" bl_label = "Archimesh" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" diff --git a/release/scripts/addons/archimesh/achm_window_panel.py b/release/scripts/addons/archimesh/achm_window_panel.py index ce7cb34e6046..592e13a66cab 100644 --- a/release/scripts/addons/archimesh/achm_window_panel.py +++ b/release/scripts/addons/archimesh/achm_window_panel.py @@ -1772,7 +1772,7 @@ class GeneralPanelProperties(PropertyGroup): # Define panel class to modify myobjects. # ------------------------------------------------------------------ class AchmWindowEditPanel(Panel): - bl_idname = "window.edit_panel2" + bl_idname = "ARCHIMESH_PT_window_edit" bl_label = "Window Panel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' diff --git a/release/scripts/addons/archipack/__init__.py b/release/scripts/addons/archipack/__init__.py index 7707abd313bc..0f5d3a86ae83 100644 --- a/release/scripts/addons/archipack/__init__.py +++ b/release/scripts/addons/archipack/__init__.py @@ -63,7 +63,7 @@ imp.reload(archipack_floor) imp.reload(archipack_rendering) - print("archipack: reload ready") + # print("archipack: reload ready") else: from . import archipack_progressbar from . import archipack_material @@ -82,7 +82,7 @@ from . import archipack_floor from . import archipack_rendering - print("archipack: ready") + # print("archipack: ready") # noinspection PyUnresolvedReferences import bpy @@ -433,9 +433,8 @@ def draw_menu(self, context): ).preset_operator = "archipack.roof" -class ARCHIPACK_create_menu(Menu): +class ARCHIPACK_MT_create(Menu): bl_label = 'Archipack' - bl_idname = 'ARCHIPACK_create_menu' def draw(self, context): draw_menu(self, context) @@ -450,7 +449,7 @@ def menu_func(self, context): # either draw sub menu or right at end of this one if context.user_preferences.addons[__name__].preferences.create_submenu: layout.operator_context = 'INVOKE_REGION_WIN' - layout.menu("ARCHIPACK_create_menu", icon_value=icons["archipack"].icon_id) + layout.menu("ARCHIPACK_MT_create", icon_value=icons["archipack"].icon_id) else: draw_menu(self, context) @@ -504,14 +503,14 @@ def register(): WindowManager.archipack = PointerProperty(type=archipack_data) bpy.utils.register_class(Archipack_Pref) update_panel(None, bpy.context) - bpy.utils.register_class(ARCHIPACK_create_menu) + bpy.utils.register_class(ARCHIPACK_MT_create) bpy.types.INFO_MT_mesh_add.append(menu_func) def unregister(): global icons_collection bpy.types.INFO_MT_mesh_add.remove(menu_func) - bpy.utils.unregister_class(ARCHIPACK_create_menu) + bpy.utils.unregister_class(ARCHIPACK_MT_create) bpy.utils.unregister_class(TOOLS_PT_Archipack_Tools) bpy.utils.unregister_class(TOOLS_PT_Archipack_Create) diff --git a/release/scripts/addons/archipack/archipack_material.py b/release/scripts/addons/archipack/archipack_material.py index c226c7fbc6d4..22da496d7360 100644 --- a/release/scripts/addons/archipack/archipack_material.py +++ b/release/scripts/addons/archipack/archipack_material.py @@ -223,6 +223,8 @@ def __init__(self): Store sets for each object type """ self.objects = {} + # hold reference of dynamic enumerator + self.enums = {} def get_filename(self, object_type): @@ -234,6 +236,7 @@ def get_filename(self, object_type): def cleanup(self): self.objects.clear() + self.enums.clear() def register_set(self, object_type, set_name, materials_names): @@ -275,16 +278,20 @@ def load(self, object_type): finally: f.close() - for s_key in material_sets.keys(): + s_keys = material_sets.keys() + for s_key in s_keys: self.register_set(object_type, s_key, material_sets[s_key]) + self.make_enum(object_type, s_keys) + def save(self, object_type): # always save in user prefs filename = self.get_filename(object_type) # print("filename:%s" % filename) o_dict = self.objects[object_type] lines = [] - for s_key in o_dict.keys(): + s_keys = o_dict.keys() + for s_key in s_keys: for mat in o_dict[s_key]: lines.append("{}##|##{}\n".format(s_key, mat)) try: @@ -296,6 +303,8 @@ def save(self, object_type): finally: f.close() + self.make_enum(object_type, s_keys) + def add(self, context, set_name): o = context.active_object if "archipack_material" in o: @@ -311,9 +320,11 @@ def remove(self, context): d = o.archipack_material[0] object_type = d.category set_name = d.material - if set_name in self.objects[object_type].keys(): + s_keys = self.objects[object_type].keys() + if set_name in s_keys: self.objects[object_type].pop(set_name) self.save(object_type) + self.make_enum(object_type, s_keys) def get_materials(self, object_type, set_name): if object_type not in self.objects.keys(): @@ -326,7 +337,13 @@ def get_materials(self, object_type, set_name): return None return self.objects[object_type][set_name] - def make_enum(self, object_type): + def make_enum(self, object_type, s_keys): + if len(s_keys) < 1: + self.enums[object_type] = [('DEFAULT', 'Default', '', 0)] + else: + self.enums[object_type] = [(s.upper(), s.capitalize(), '', i) for i, s in enumerate(s_keys)] + + def get_enum(self, object_type): if object_type not in self.objects.keys(): self.load(object_type) @@ -334,19 +351,14 @@ def make_enum(self, object_type): if object_type not in self.objects.keys(): self.objects[object_type] = {} - s_keys = self.objects[object_type].keys() - - if len(s_keys) < 1: - return [('DEFAULT', 'Default', '', 0)] - - return [(s.upper(), s.capitalize(), '', i) for i, s in enumerate(s_keys)] + return self.enums[object_type] def material_enum(self, context): global setman if setman is None: setman = MaterialSetManager() - return setman.make_enum(self.category) + return setman.get_enum(self.category) def update(self, context): diff --git a/release/scripts/addons/archipack/archipack_preset.py b/release/scripts/addons/archipack/archipack_preset.py index 3541042c3f55..dcaa7970878d 100644 --- a/release/scripts/addons/archipack/archipack_preset.py +++ b/release/scripts/addons/archipack/archipack_preset.py @@ -34,6 +34,8 @@ ThumbHandle, Screen, GlRect, GlPolyline, GlPolygon, GlText, GlHandle ) +preset_paths = bpy.utils.script_paths("presets") +addons_paths = bpy.utils.script_paths("addons") class CruxHandle(GlHandle): @@ -164,10 +166,6 @@ def sensor_center(self): return self.pos_3d -preset_paths = bpy.utils.script_paths("presets") -addons_paths = bpy.utils.script_paths("addons") - - class PresetMenuItem(): def __init__(self, thumbsize, preset, image=None): name = bpy.path.display_name_from_filepath(preset) @@ -246,6 +244,7 @@ def load_default_image(self): def scan_files(self, category): file_list = [] + """ # load default presets dir_path = os.path.dirname(os.path.realpath(__file__)) sub_path = "presets" + os.path.sep + category @@ -255,6 +254,7 @@ def scan_files(self, category): for f in os.listdir(presets_path) if f.endswith('.py') and not f.startswith('.')] + """ # load user def presets for path in preset_paths: presets_path = os.path.join(path, category) @@ -524,7 +524,6 @@ def remove(self, context, filepath): os.remove(filepath[:-3] + ".png") def background_render(self, context, cls, preset): - print("bg render") generator = os.path.dirname(os.path.realpath(__file__)) + os.path.sep + "archipack_thumbs.py" # Run external instance of blender like the original thumbnail generator. cmd = [ @@ -536,7 +535,7 @@ def background_render(self, context, cls, preset): "cls:" + cls, "preset:" + preset ] - print(repr(cmd)) + # print(repr(cmd)) subprocess.Popen(cmd) diff --git a/release/scripts/addons/archipack/archipack_rendering.py b/release/scripts/addons/archipack/archipack_rendering.py index 05373a75fad3..fced24acde81 100644 --- a/release/scripts/addons/archipack/archipack_rendering.py +++ b/release/scripts/addons/archipack/archipack_rendering.py @@ -30,6 +30,7 @@ import bpy # noinspection PyUnresolvedReferences import bgl +from shutil import copyfile from os import path, remove, listdir from sys import exc_info import subprocess @@ -38,14 +39,21 @@ # noinspection PyUnresolvedReferences from math import ceil from bpy.types import Operator +from bl_ui import properties_render class ARCHIPACK_OT_render_thumbs(Operator): bl_idname = "archipack.render_thumbs" - bl_label = "Render preset thumbs" - bl_description = "Render all presets thumbs" + bl_label = "Render presets thumbs" + bl_description = "Setup default presets and update thumbs" bl_options = {'REGISTER', 'INTERNAL'} + @classmethod + def poll(cls, context): + # Ensure CYCLES engine is available + return context.scene.archipack_progress < 0 and \ + 'CYCLES' in properties_render.RENDER_PT_render.COMPAT_ENGINES + def background_render(self, context, cls, preset): generator = path.dirname(path.realpath(__file__)) + path.sep + "archipack_thumbs.py" addon_name = __name__.split('.')[0] @@ -63,24 +71,61 @@ def background_render(self, context, cls, preset): "cls:" + cls, "preset:" + preset ] - popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) for stdout_line in iter(popen.stdout.readline, ""): yield stdout_line popen.stdout.close() popen.wait() - def scan_files(self, category): + def copy_to_user_path(self, category): + """ + Copy factory presets to writeable presets folder + Two cases here: + 1 there is not presets thumbs found (official version) + 2 thumbs allready are there (unofficial) + """ file_list = [] # load default presets dir_path = path.dirname(path.realpath(__file__)) sub_path = "presets" + path.sep + category - presets_path = path.join(dir_path, sub_path) - if path.exists(presets_path): - file_list += [presets_path + path.sep + f[:-3] - for f in listdir(presets_path) - if f.endswith('.py') and - not f.startswith('.')] + source_path = path.join(dir_path, sub_path) + if path.exists(source_path): + file_list.extend([f + for f in listdir(source_path) + if (f.endswith('.py') or f.endswith('.txt')) and + not f.startswith('.')]) + + target_path = path.join("presets", category) + presets_path = bpy.utils.user_resource('SCRIPTS', + target_path, + create=True) + # files from factory not found in user dosent require a recompute + skipfiles = [] + for f in file_list: + # copy python/txt preset + if not path.exists(presets_path + path.sep + f): + copyfile(source_path + path.sep + f, presets_path + path.sep + f) + + # skip txt files (material presets) + if f.endswith(".txt"): + skipfiles.append(f) + + # when thumbs allready are in factory folder but not found in user one + # simply copy them, and add preset to skip list + thumb_filename = f[:-3] + ".png" + if path.exists(source_path + path.sep + thumb_filename): + if not path.exists(presets_path + path.sep + thumb_filename): + copyfile(source_path + path.sep + thumb_filename, presets_path + path.sep + thumb_filename) + skipfiles.append(f) + + return skipfiles + + def scan_files(self, category): + file_list = [] + + # copy from factory to user writeable folder + skipfiles = self.copy_to_user_path(category) + # load user def presets preset_paths = bpy.utils.script_paths("presets") for preset in preset_paths: @@ -89,7 +134,8 @@ def scan_files(self, category): file_list += [presets_path + path.sep + f[:-3] for f in listdir(presets_path) if f.endswith('.py') and - not f.startswith('.')] + not f.startswith('.') and + f not in skipfiles] file_list.sort() return file_list @@ -99,7 +145,7 @@ def rebuild_thumbs(self, context): dir_path = path.dirname(path.realpath(__file__)) sub_path = "presets" presets_path = path.join(dir_path, sub_path) - print(presets_path) + # print(presets_path) if path.exists(presets_path): dirs = listdir(presets_path) for dir in dirs: @@ -119,11 +165,12 @@ def rebuild_thumbs(self, context): # elif not "Fra:1" in l: # print(l.strip()) - @classmethod - def poll(cls, context): - return context.scene.archipack_progress < 0 - def invoke(self, context, event): + addon_name = __name__.split('.')[0] + matlib_path = context.user_preferences.addons[addon_name].preferences.matlib_path + + if matlib_path == '': + self.report({'WARNING'}, "You should setup a default material library path in addon preferences") return context.window_manager.invoke_confirm(self, event) def execute(self, context): diff --git a/release/scripts/addons/blender_id/__init__.py b/release/scripts/addons/blender_id/__init__.py index a7094c7f6ab8..73371945d224 100644 --- a/release/scripts/addons/blender_id/__init__.py +++ b/release/scripts/addons/blender_id/__init__.py @@ -21,7 +21,7 @@ bl_info = { 'name': 'Blender ID authentication', 'author': 'Francesco Siddi, Inês Almeida and Sybren A. Stüvel', - 'version': (1, 3, 0), + 'version': (1, 4, 1), 'blender': (2, 77, 0), 'location': 'Add-on preferences', 'description': @@ -159,8 +159,9 @@ def token_expires() -> typing.Optional[datetime.datetime]: # Try parsing as different formats. A new Blender ID is coming, # which may change the format in which timestamps are sent. formats = [ - '%Y-%m-%dT%H:%M:%S.%fZ', # ISO 8601 with Z-suffix, used by new Blender ID - '%a, %d %b %Y %H:%M:%S GMT', # RFC 1123, used by current Blender ID + '%Y-%m-%dT%H:%M:%SZ', # ISO 8601 with Z-suffix + '%Y-%m-%dT%H:%M:%S.%fZ', # ISO 8601 with fractional seconds and Z-suffix + '%a, %d %b %Y %H:%M:%S GMT', # RFC 1123, used by old Blender ID ] for fmt in formats: try: @@ -217,11 +218,9 @@ def draw(self, context): if active_profile: expiry = token_expires() now = datetime.datetime.utcnow() - show_validate_button = bpy.app.debug if expiry is None: layout.label(text='We do not know when your token expires, please validate it.') - show_validate_button = True elif now >= expiry: layout.label(text='Your login has expired! Log out and log in again to refresh it.', icon='ERROR') @@ -247,10 +246,9 @@ def draw(self, context): layout.label('You are logged in as %s. Your authentication token expires %s.' % (active_profile.username, exp_str), icon='WORLD_DATA') - row = layout.row() + row = layout.row().split(0.8) row.operator('blender_id.logout') - if show_validate_button: - row.operator('blender_id.validate') + row.operator('blender_id.validate') else: layout.prop(self, 'blender_id_username') layout.prop(self, 'blender_id_password') diff --git a/release/scripts/addons/bone_selection_sets.py b/release/scripts/addons/bone_selection_sets.py index 46aa7f252458..b08201a7b253 100644 --- a/release/scripts/addons/bone_selection_sets.py +++ b/release/scripts/addons/bone_selection_sets.py @@ -18,8 +18,8 @@ bl_info = { "name": "Bone Selection Sets", - "author": "Inês Almeida, Antony Riakiotakis, Dan Eicher", - "version": (2, 0, 1), + "author": "Inês Almeida, Sybren A. Stüvel, Antony Riakiotakis, Dan Eicher", + "version": (2, 1, 0), "blender": (2, 75, 0), "location": "Properties > Object Data (Armature) > Selection Sets", "description": "List of Bone sets for easy selection while animating", @@ -31,18 +31,18 @@ import bpy from bpy.types import ( - Operator, - Menu, - Panel, - UIList, - PropertyGroup, - ) + Operator, + Menu, + Panel, + UIList, + PropertyGroup, +) from bpy.props import ( - StringProperty, - IntProperty, - EnumProperty, - CollectionProperty, - ) + StringProperty, + IntProperty, + EnumProperty, + CollectionProperty, +) # Data Structure ############################################################## @@ -68,6 +68,8 @@ def draw(self, context): layout.operator("pose.selection_set_delete_all", icon='X') layout.operator("pose.selection_set_remove_bones", icon='X') + layout.operator("pose.selection_set_copy", icon='COPYDOWN') + layout.operator("pose.selection_set_paste", icon='PASTEDOWN') class POSE_PT_selection_sets(Panel): @@ -129,20 +131,34 @@ def draw_item(self, context, layout, data, set, icon, active_data, active_propna class POSE_MT_create_new_selection_set(Menu): - bl_idname = "POSE_MT_selection_set_create" bl_label = "Choose Selection Set" def draw(self, context): layout = self.layout layout.operator("pose.selection_set_add_and_assign", - text="New Selection Set") + text="New Selection Set") + + +class POSE_MT_selection_sets(Menu): + bl_label = 'Select Selection Set' + + @classmethod + def poll(cls, context): + return POSE_OT_selection_set_select.poll(context) + + def draw(self, context): + layout = self.layout + layout.operator_context = 'EXEC_DEFAULT' + for idx, sel_set in enumerate(context.object.selection_sets): + props = layout.operator(POSE_OT_selection_set_select.bl_idname, text=sel_set.name) + props.selection_set_index = idx # Operators ################################################################### class PluginOperator(Operator): @classmethod - def poll(self, context): + def poll(cls, context): return (context.object and context.object.type == 'ARMATURE' and context.mode == 'POSE') @@ -150,12 +166,11 @@ def poll(self, context): class NeedSelSetPluginOperator(PluginOperator): @classmethod - def poll(self, context): - if super().poll(context): - arm = context.object - return (arm.active_selection_set < len(arm.selection_sets) and - arm.active_selection_set >= 0) - return False + def poll(cls, context): + if not super().poll(context): + return False + arm = context.object + return 0 <= arm.active_selection_set < len(arm.selection_sets) class POSE_OT_selection_set_delete_all(PluginOperator): @@ -206,11 +221,11 @@ class POSE_OT_selection_set_move(NeedSelSetPluginOperator): ) @classmethod - def poll(self, context): - if super().poll(context): - arm = context.object - return len(arm.selection_sets) > 1 - return False + def poll(cls, context): + if not super().poll(context): + return False + arm = context.object + return len(arm.selection_sets) > 1 def execute(self, context): arm = context.object @@ -235,30 +250,12 @@ class POSE_OT_selection_set_add(PluginOperator): def execute(self, context): arm = context.object - - new_sel_set = arm.selection_sets.add() - - # naming - if "SelectionSet" not in arm.selection_sets: - new_sel_set.name = "SelectionSet" - else: - sorted_sets = [] - for selset in arm.selection_sets: - if selset.name.startswith("SelectionSet."): - index = selset.name[13:] - if index.isdigit(): - sorted_sets.append(index) - sorted_sets = sorted(sorted_sets) - min_index = 1 - for num in sorted_sets: - num = int(num) - if min_index < num: - break - min_index = num + 1 - new_sel_set.name = "SelectionSet.{:03d}".format(min_index) + sel_sets = arm.selection_sets + new_sel_set = sel_sets.add() + new_sel_set.name = uniqify("SelectionSet", sel_sets.keys()) # select newly created set - arm.active_selection_set = len(arm.selection_sets) - 1 + arm.active_selection_set = len(sel_sets) - 1 return {'FINISHED'} @@ -293,7 +290,7 @@ def invoke(self, context, event): if not (arm.active_selection_set < len(arm.selection_sets)): bpy.ops.wm.call_menu("INVOKE_DEFAULT", - name="POSE_MT_selection_set_create") + name="POSE_MT_selection_set_create") else: bpy.ops.pose.selection_set_assign('EXEC_DEFAULT') @@ -337,12 +334,22 @@ class POSE_OT_selection_set_select(NeedSelSetPluginOperator): bl_description = "Add Selection Set bones to current selection" bl_options = {'UNDO', 'REGISTER'} + selection_set_index = IntProperty( + name='Selection Set Index', + default=-1, + description='Which Selection Set to select; -1 uses the active Selection Set') + def execute(self, context): arm = context.object - act_sel_set = arm.selection_sets[arm.active_selection_set] + + if self.selection_set_index == -1: + idx = arm.active_selection_set + else: + idx = self.selection_set_index + sel_set = arm.selection_sets[idx] for bone in context.visible_pose_bones: - if bone.name in act_sel_set.bone_ids: + if bone.name in sel_set.bone_ids: bone.bone.select = True return {'FINISHED'} @@ -377,11 +384,44 @@ def execute(self, context): return {'FINISHED'} +class POSE_OT_selection_set_copy(NeedSelSetPluginOperator): + bl_idname = "pose.selection_set_copy" + bl_label = "Copy Selection Set to Clipboard" + bl_description = "Converts the Selection Set to JSON and places it on the clipboard" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + context.window_manager.clipboard = to_json(context) + self.report({'INFO'}, 'Copied Selection Set to Clipboard') + return {'FINISHED'} + + +class POSE_OT_selection_set_paste(PluginOperator): + bl_idname = "pose.selection_set_paste" + bl_label = "Paste Selection Set from Clipboard" + bl_description = "Adds a new Selection Set from copied JSON on the clipboard" + bl_options = {'UNDO', 'REGISTER'} + + def execute(self, context): + import json + + try: + from_json(context, context.window_manager.clipboard) + except (json.JSONDecodeError, KeyError): + self.report({'ERROR'}, 'The clipboard does not contain a Selection Set') + else: + # Select the pasted Selection Set. + context.object.active_selection_set = len(context.object.selection_sets) - 1 + + return {'FINISHED'} + + # Registry #################################################################### classes = ( POSE_MT_create_new_selection_set, POSE_MT_selection_sets_specials, + POSE_MT_selection_sets, POSE_PT_selection_sets, POSE_UL_selection_set, SelectionEntry, @@ -396,23 +436,106 @@ def execute(self, context): POSE_OT_selection_set_select, POSE_OT_selection_set_deselect, POSE_OT_selection_set_add_and_assign, + POSE_OT_selection_set_copy, + POSE_OT_selection_set_paste, ) +def add_sss_button(self, context): + self.layout.menu('POSE_MT_selection_sets') + + +def to_json(context) -> str: + """Convert the active bone selection set of the current rig to JSON.""" + import json + + arm = context.object + active_idx = arm.active_selection_set + sel_set = arm.selection_sets[active_idx] + + return json.dumps({ + 'name': sel_set.name, + 'bones': [bone_id.name for bone_id in sel_set.bone_ids] + }) + + +def from_json(context, as_json: str): + """Add the single bone selection set from JSON to the current rig.""" + import json + + sel_set = json.loads(as_json) + + sel_sets = context.object.selection_sets + new_sel_set = sel_sets.add() + new_sel_set.name = uniqify(sel_set['name'], sel_sets.keys()) + + for bone_name in sel_set['bones']: + bone_id = new_sel_set.bone_ids.add() + bone_id.name = bone_name + + +def uniqify(name: str, other_names: list) -> str: + """Return a unique name with .xxx suffix if necessary. + + >>> uniqify('hey', ['there']) + 'hey' + >>> uniqify('hey', ['hey.001', 'hey.005']) + 'hey' + >>> uniqify('hey', ['hey', 'hey.001', 'hey.005']) + 'hey.002' + >>> uniqify('hey', ['hey', 'hey.005', 'hey.001']) + 'hey.002' + >>> uniqify('hey', ['hey', 'hey.005', 'hey.001', 'hey.left']) + 'hey.002' + >>> uniqify('hey', ['hey', 'hey.001', 'hey.002']) + 'hey.003' + + It also works with a dict_keys object: + >>> uniqify('hey', {'hey': 1, 'hey.005': 1, 'hey.001': 1}.keys()) + 'hey.002' + """ + + if name not in other_names: + return name + + # Construct the list of numbers already in use. + offset = len(name) + 1 + others = (n[offset:] for n in other_names + if n.startswith(name + '.')) + numbers = sorted(int(suffix) for suffix in others + if suffix.isdigit()) + + # Find the first unused number. + min_index = 1 + for num in numbers: + if min_index < num: + break + min_index = num + 1 + return "{}.{:03d}".format(name, min_index) + + def register(): for cls in classes: bpy.utils.register_class(cls) bpy.types.Object.selection_sets = CollectionProperty( - type=SelectionSet, - name="Selection Sets", - description="List of groups of bones for easy selection" - ) + type=SelectionSet, + name="Selection Sets", + description="List of groups of bones for easy selection" + ) bpy.types.Object.active_selection_set = IntProperty( - name="Active Selection Set", - description="Index of the currently active selection set", - default=0 - ) + name="Active Selection Set", + description="Index of the currently active selection set", + default=0 + ) + + wm = bpy.context.window_manager + km = wm.keyconfigs.active.keymaps['Pose'] + + kmi = km.keymap_items.new('wm.call_menu', 'W', 'PRESS', alt=True, shift=True) + kmi.properties.name = 'POSE_MT_selection_sets' + + bpy.types.VIEW3D_MT_select_pose.append(add_sss_button) def unregister(): @@ -424,4 +547,7 @@ def unregister(): if __name__ == "__main__": + import doctest + + doctest.testmod() register() diff --git a/release/scripts/addons/btrace/__init__.py b/release/scripts/addons/btrace/__init__.py index c1f62512ff05..8f00f2c33368 100644 --- a/release/scripts/addons/btrace/__init__.py +++ b/release/scripts/addons/btrace/__init__.py @@ -39,27 +39,27 @@ import bpy from bpy.types import AddonPreferences from .bTrace_props import ( - TracerProperties, - addTracerObjectPanel, - ) + TracerProperties, + addTracerObjectPanel, +) from .bTrace import ( - OBJECT_OT_convertcurve, - OBJECT_OT_objecttrace, - OBJECT_OT_objectconnect, - OBJECT_OT_writing, - OBJECT_OT_particletrace, - OBJECT_OT_traceallparticles, - OBJECT_OT_curvegrow, - OBJECT_OT_reset, - OBJECT_OT_fcnoise, - OBJECT_OT_meshfollow, - OBJECT_OT_materialChango, - OBJECT_OT_clearColorblender, - ) + OBJECT_OT_convertcurve, + OBJECT_OT_objecttrace, + OBJECT_OT_objectconnect, + OBJECT_OT_writing, + OBJECT_OT_particletrace, + OBJECT_OT_traceallparticles, + OBJECT_OT_curvegrow, + OBJECT_OT_reset, + OBJECT_OT_fcnoise, + OBJECT_OT_meshfollow, + OBJECT_OT_materialChango, + OBJECT_OT_clearColorblender, +) from bpy.props import ( - EnumProperty, - PointerProperty, - ) + EnumProperty, + PointerProperty, +) # Add-on Preferences diff --git a/release/scripts/addons/btrace/bTrace_props.py b/release/scripts/addons/btrace/bTrace_props.py index bff3b88f049c..70c57532ce32 100644 --- a/release/scripts/addons/btrace/bTrace_props.py +++ b/release/scripts/addons/btrace/bTrace_props.py @@ -568,6 +568,7 @@ class TracerProperties(PropertyGroup): # Draw Brush panel in Toolbar class addTracerObjectPanel(Panel): + bl_idname = "BTRACE_PT_object_brush" bl_label = "Btrace" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" diff --git a/release/scripts/addons/development_ui_classes.py b/release/scripts/addons/development_ui_classes.py index 18b692f42358..f03b7bb50b8a 100644 --- a/release/scripts/addons/development_ui_classes.py +++ b/release/scripts/addons/development_ui_classes.py @@ -21,7 +21,7 @@ bl_info = { "name": "UI Classes Overview", "author": "lijenstina", - "version": (1, 0, 1), + "version": (1, 0, 2), "blender": (2, 78, 0), "location": "Text Editor > Properties", "description": "Print the UI classes in a text-block", @@ -33,15 +33,15 @@ import bpy from bpy.types import ( - Operator, - Panel, - PropertyGroup, - ) + Operator, + Panel, + PropertyGroup, +) from bpy.props import ( - BoolProperty, - EnumProperty, - PointerProperty, - ) + BoolProperty, + EnumProperty, + PointerProperty, +) class TEXT_PT_ui_cheat_sheet(Panel): @@ -128,7 +128,10 @@ def execute(self, context): textblock.write(('\n' if not searchable else '').join(sorted(op_string_ui[cls_name]))) textblock.write(close_line) - context.space_data.text = bpy.data.texts[file_name] + # try to set the created text block to active + if context.area.type in {"TEXT_EDITOR"}: + bpy.context.space_data.text = bpy.data.texts[file_name] + self.report({'INFO'}, "See %s textblock" % file_name) return {'FINISHED'} @@ -152,21 +155,21 @@ class text_ui_cheat_props(PropertyGroup): ('Header', "Headers", "Print Header UI types"), ), default='all', - ) + ) searchable = BoolProperty( name="Format searchable", description="Generate the list as Python dictionary,\n" "using the format Class: Path", default=False - ) + ) # Register classes = ( - TEXT_OT_ui_cheat_sheet, - TEXT_PT_ui_cheat_sheet, - text_ui_cheat_props - ) + TEXT_OT_ui_cheat_sheet, + TEXT_PT_ui_cheat_sheet, + text_ui_cheat_props +) def register(): diff --git a/release/scripts/addons/io_export_after_effects.py b/release/scripts/addons/io_export_after_effects.py index aafa8ecbb429..4a5c6b9282bb 100644 --- a/release/scripts/addons/io_export_after_effects.py +++ b/release/scripts/addons/io_export_after_effects.py @@ -23,8 +23,8 @@ "description": "Export cameras, selected objects & camera solution " "3D Markers to Adobe After Effects CS3 and above", "author": "Bartek Skorupa", - "version": (0, 64), - "blender": (2, 69, 0), + "version": (0, 65), + "blender": (2, 79, 0), "location": "File > Export > Adobe After Effects (.jsx)", "warning": "", "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" @@ -150,7 +150,7 @@ def convert_name(name): # get object's blender's location rotation and scale and return AE's Position, Rotation/Orientation and scale # this function will be called for every object for every frame -def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=False): +def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=False, ae_size=100.0): # get blender transform data for ob b_loc = matrix.to_translation() @@ -159,9 +159,9 @@ def convert_transform_matrix(matrix, width, height, aspect, x_rot_correction=Fal # convert to AE Position Rotation and Scale # Axes in AE are different. AE's X is blender's X, AE's Y is negative Blender's Z, AE's Z is Blender's Y - x = (b_loc.x * 100.0) / aspect + width / 2.0 # calculate AE's X position - y = (-b_loc.z * 100.0) + (height / 2.0) # calculate AE's Y position - z = b_loc.y * 100.0 # calculate AE's Z position + x = (b_loc.x * ae_size) / aspect + width / 2.0 # calculate AE's X position + y = (-b_loc.z * ae_size) + (height / 2.0) # calculate AE's Y position + z = b_loc.y * ae_size # calculate AE's Z position # Convert rotations to match AE's orientation. rx = degrees(b_rot.x) # if not x_rot_correction - AE's X orientation = blender's X rotation if 'ZYX' euler. ry = -degrees(b_rot.y) # AE's Y orientation is negative blender's Y rotation if 'ZYX' euler @@ -240,7 +240,7 @@ def convert_lens(camera, width, height, aspect): # jsx script for AE creation -def write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles): +def write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles, ae_size): print("\n---------------------------\n- Export to After Effects -\n---------------------------") # store the current frame to restore it at the end of export @@ -382,7 +382,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam, # bundles are in camera space. Transpose to world space matrix = Matrix.Translation(cam.matrix_basis.copy() * track.bundle) # convert the position into AE space - ae_transform = convert_transform_matrix(matrix, data['width'], data['height'], data['aspect'], x_rot_correction=False) + ae_transform = convert_transform_matrix(matrix, data['width'], data['height'], data['aspect'], False, ae_size) js_data['bundles_cam'][name_ae]['position'] += '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2]) # get all keyframes for each object and store in dico @@ -407,7 +407,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam, # get cam name name_ae = active_cam_name # convert cam transform properties to AE space - ae_transform = convert_transform_matrix(active_cam.matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True) + ae_transform = convert_transform_matrix(active_cam.matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size) # convert Blender's lens to AE's zoom in pixels zoom = convert_lens(active_cam, data['width'], data['height'], data['aspect']) # store all values in dico @@ -437,7 +437,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam, # get cam name name_ae = selection['cameras'][i][1] # convert cam transform properties to AE space - ae_transform = convert_transform_matrix(cam[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True) + ae_transform = convert_transform_matrix(cam[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size) # convert Blender's lens to AE's zoom in pixels zoom = convert_lens(cam[0], data['width'], data['height'], data['aspect']) # store all values in dico @@ -475,7 +475,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam, name_ae = selection['lights'][i][1] type = selection['lights'][i][0].data.type # convert ob transform properties to AE space - ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True) + ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size) color = ob[0].data.color # store all values in dico position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2]) @@ -522,7 +522,7 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam, # get object name name_ae = selection['nulls'][i][1] # convert ob transform properties to AE space - ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], x_rot_correction=True) + ae_transform = convert_transform_matrix(ob[0].matrix_world.copy(), data['width'], data['height'], data['aspect'], True, ae_size) # store all values in dico position = '[%f,%f,%f],' % (ae_transform[0], ae_transform[1], ae_transform[2]) orientation = '[%f,%f,%f],' % (ae_transform[3], ae_transform[4], ae_transform[5]) @@ -676,10 +676,10 @@ def write_jsx_file(file, data, selection, include_animation, include_active_cam, ########################################## -def main(file, context, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles): +def main(file, context, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles, ae_size): data = get_comp_data(context) selection = get_selected(context) - write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles) + write_jsx_file(file, data, selection, include_animation, include_active_cam, include_selected_cams, include_selected_objects, include_cam_bundles, ae_size) print ("\nExport to After Effects Completed") return {'FINISHED'} @@ -688,7 +688,7 @@ def main(file, context, include_animation, include_active_cam, include_selected_ ########################################## from bpy_extras.io_utils import ExportHelper -from bpy.props import StringProperty, BoolProperty +from bpy.props import StringProperty, BoolProperty, FloatProperty class ExportJsx(bpy.types.Operator, ExportHelper): @@ -728,11 +728,18 @@ class ExportJsx(bpy.types.Operator, ExportHelper): # description="Include 3D Markers of Object Motion Solution for selected cameras", # default=True, # ) + ae_size = FloatProperty( + name="AE Size", + description="Size of AE Composition (pixels per 1BU)", + default=100.0, + ) def draw(self, context): layout = self.layout box = layout.box() + box.label('Size fo AE Comp (pixels per 1 BU)') + box.prop(self, 'ae_size') box.label('Animation:') box.prop(self, 'include_animation') box.label('Include Cameras and Objects:') @@ -752,7 +759,7 @@ def poll(cls, context): return ok def execute(self, context): - return main(self.filepath, context, self.include_animation, self.include_active_cam, self.include_selected_cams, self.include_selected_objects, self.include_cam_bundles) + return main(self.filepath, context, self.include_animation, self.include_active_cam, self.include_selected_cams, self.include_selected_objects, self.include_cam_bundles, self.ae_size) def menu_func(self, context): diff --git a/release/scripts/addons/io_mesh_ply/__init__.py b/release/scripts/addons/io_mesh_ply/__init__.py index 268ad2273b3b..6497322639d4 100644 --- a/release/scripts/addons/io_mesh_ply/__init__.py +++ b/release/scripts/addons/io_mesh_ply/__init__.py @@ -134,7 +134,7 @@ class ExportPLY(bpy.types.Operator, ExportHelper, IOPLYOrientationHelper): @classmethod def poll(cls, context): - return context.active_object != None + return context.active_object is not None def execute(self, context): from . import export_ply diff --git a/release/scripts/addons/io_mesh_ply/export_ply.py b/release/scripts/addons/io_mesh_ply/export_ply.py index d4db57107f13..8de5d67451b3 100644 --- a/release/scripts/addons/io_mesh_ply/export_ply.py +++ b/release/scripts/addons/io_mesh_ply/export_ply.py @@ -154,7 +154,8 @@ def rvec2d(v): if use_colors: fw("property uchar red\n" "property uchar green\n" - "property uchar blue\n") + "property uchar blue\n" + "property uchar alpha\n") fw("element face %d\n" % len(mesh.tessfaces)) fw("property list uchar uint vertex_indices\n") @@ -167,7 +168,7 @@ def rvec2d(v): if use_uv_coords: fw(" %.6f %.6f" % v[2]) # uv if use_colors: - fw(" %u %u %u" % v[3]) # col + fw(" %u %u %u %u" % v[3]) # col fw("\n") for pf in ply_faces: diff --git a/release/scripts/addons/io_mesh_ply/import_ply.py b/release/scripts/addons/io_mesh_ply/import_ply.py index eb097ba9249f..86fc2e1b06ad 100644 --- a/release/scripts/addons/io_mesh_ply/import_ply.py +++ b/release/scripts/addons/io_mesh_ply/import_ply.py @@ -242,7 +242,7 @@ def load_ply_mesh(filepath, ply_name): uvindices = (el.index(b's'), el.index(b't')) if -1 in uvindices: uvindices = None - colindices = el.index(b'red'), el.index(b'green'), el.index(b'blue') + colindices = el.index(b'red'), el.index(b'green'), el.index(b'blue'), el.index(b'alpha') if -1 in colindices: colindices = None else: # if not a float assume uchar @@ -267,6 +267,7 @@ def add_face(vertices, indices, uvindices, colindices): mesh_colors.append([(vertices[index][colindices[0]] * colmultiply[0], vertices[index][colindices[1]] * colmultiply[1], vertices[index][colindices[2]] * colmultiply[2], + vertices[index][colindices[3]] * colmultiply[3], ) for index in indices]) if uvindices or colindices: @@ -340,7 +341,10 @@ def add_face(vertices, indices, uvindices, colindices): f_col = f.color1, f.color2, f.color3 for j, col in enumerate(f_col): - col.r, col.g, col.b = ply_col[j] + col[0] = ply_col[j][0] + col[1] = ply_col[j][1] + col[2] = ply_col[j][2] + col[3] = ply_col[j][3] mesh.validate() mesh.update() diff --git a/release/scripts/addons/io_online_sketchfab/pack_for_export.py b/release/scripts/addons/io_online_sketchfab/pack_for_export.py index 16d38cca1fc9..e9fbde72bef9 100644 --- a/release/scripts/addons/io_online_sketchfab/pack_for_export.py +++ b/release/scripts/addons/io_online_sketchfab/pack_for_export.py @@ -61,17 +61,31 @@ def prepare_assets(export_settings): for ob in bpy.data.objects: if ob.type == 'MESH': + for mat_slot in ob.material_slots: - if not mat_slot.material: - continue - for tex_slot in mat_slot.material.texture_slots: - if not tex_slot: + #CYCLES RENDER + if bpy.context.scene.render.engine == 'CYCLES': + if not mat_slot.material: + continue + if not mat_slot.material.node_tree: continue - tex = tex_slot.texture - if tex.type == 'IMAGE': - image = tex.image - if image is not None: - images.add(image) + imgnodes = [n for n in mat_slot.material.node_tree.nodes if n.type == 'TEX_IMAGE'] + for node in imgnodes: + if node.image is not None: + images.add(node.image) + #BLENDER RENDER + else: + if not mat_slot.material: + continue + for tex_slot in mat_slot.material.texture_slots: + if not tex_slot: + continue + tex = tex_slot.texture + if tex.type == 'IMAGE': + image = tex.image + if image is not None: + images.add(image) + if ((export_settings['models'] == 'SELECTION' and ob.type == 'MESH') or (export_settings['lamps'] == 'SELECTION' and ob.type == 'LAMP')): diff --git a/release/scripts/addons/io_scene_obj/import_obj.py b/release/scripts/addons/io_scene_obj/import_obj.py index c120e540aab5..c4275606789c 100644 --- a/release/scripts/addons/io_scene_obj/import_obj.py +++ b/release/scripts/addons/io_scene_obj/import_obj.py @@ -109,7 +109,7 @@ def load_material_image(blender_material, mat_wrap, use_cycles, context_material curr_token = [] for token in img_data[:-1]: - if token.startswith(b'-'): + if token.startswith(b'-') and token[1:].isalpha(): if curr_token: map_options[curr_token[0]] = curr_token[1:] curr_token[:] = [] diff --git a/release/scripts/addons/light_field_tools/__init__.py b/release/scripts/addons/light_field_tools/__init__.py index 66c7d61a6336..386e767af183 100644 --- a/release/scripts/addons/light_field_tools/__init__.py +++ b/release/scripts/addons/light_field_tools/__init__.py @@ -125,8 +125,8 @@ class LightFieldPropertyGroup(PropertyGroup): # Define Panel classes for updating panels = ( - light_field_tools.VIEW3D_OT_lightfield_tools, - ) + light_field_tools.VIEW3D_PT_lightfield_tools, +) def update_panel(self, context): diff --git a/release/scripts/addons/light_field_tools/light_field_tools.py b/release/scripts/addons/light_field_tools/light_field_tools.py index f32f0a9b97ad..b4693d52e24e 100644 --- a/release/scripts/addons/light_field_tools/light_field_tools.py +++ b/release/scripts/addons/light_field_tools/light_field_tools.py @@ -384,7 +384,7 @@ def execute(self, context): return {'FINISHED'} -class VIEW3D_OT_lightfield_tools(Panel): +class VIEW3D_PT_lightfield_tools(Panel): bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" bl_context = "objectmode" diff --git a/release/scripts/addons/materials_utils/__init__.py b/release/scripts/addons/materials_utils/__init__.py index 1cfec059e202..ea85a104177c 100644 --- a/release/scripts/addons/materials_utils/__init__.py +++ b/release/scripts/addons/materials_utils/__init__.py @@ -26,7 +26,7 @@ bl_info = { "name": "Materials Utils Specials", "author": "Community", - "version": (1, 0, 3), + "version": (1, 0, 5), "blender": (2, 77, 0), "location": "Materials Properties Specials > Shift Q", "description": "Materials Utils and Convertors", @@ -34,7 +34,7 @@ "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/3D_interaction/Materials_Utils", "category": "Material" - } +} if "bpy" in locals(): import importlib @@ -51,27 +51,35 @@ import bpy import os from os import ( - path as os_path, - access as os_access, - remove as os_remove, - ) + path as os_path, + access as os_access, + remove as os_remove, +) from bpy.props import ( - StringProperty, - BoolProperty, - EnumProperty, - PointerProperty, - ) + BoolProperty, + CollectionProperty, + EnumProperty, + IntProperty, + StringProperty, + PointerProperty, +) from bpy.types import ( - Menu, - Operator, - Panel, - AddonPreferences, - PropertyGroup, - ) + AddonPreferences, + Menu, + Operator, + Panel, + PropertyGroup, + UIList, +) from .warning_messages_utils import ( - warning_messages, - c_data_has_materials, - ) + warning_messages, + c_data_has_materials, + c_obj_data_has_materials, +) + +# Globals +UNDO_MESSAGE = "*Only Undo is available*" +COLUMN_SPLIT = 20 # Functions @@ -146,7 +154,7 @@ def replace_material(m1, m2, all_objects=False, update_selection=False, operator if m.material == matorg: m.material = matrep # don't break the loop as the material can be - # ref'd more than once + # referenced more than once # Indicate which objects were affected if update_selection: @@ -169,7 +177,7 @@ def select_material_by_name(find_mat_name): if find_mat is None: return - # check for editmode + # check for edit mode editmode = False scn = bpy.context.scene @@ -200,7 +208,7 @@ def select_material_by_name(find_mat_name): else: ob.select = False else: - # it's editmode, so select the polygons + # it's edit mode, so select the polygons ob = actob ms = ob.material_slots @@ -227,7 +235,7 @@ def select_material_by_name(find_mat_name): def mat_to_texface(operator=None): # assigns the first image in each material to the polygons in the active - # uvlayer for all selected objects + # uv layer for all selected objects # check for editmode editmode = False @@ -282,7 +290,7 @@ def mat_to_texface(operator=None): bpy.ops.mesh.uv_texture_add() scn.objects.active = actob - # get active uvlayer + # get active uv layer for t in me.uv_textures: if t.active: uvtex = t.data @@ -316,7 +324,7 @@ def assignmatslots(ob, matlist): scn.objects.active = ob for s in ob.material_slots: - bpy.ops.object.material_slot_remove() + remove_material_slot() # re-add them and assign material if matlist: @@ -330,7 +338,7 @@ def assignmatslots(ob, matlist): # to face indices, mat tries to get an '' as mat index pass - # restore active object: + # restore active object scn.objects.active = ob_active @@ -352,65 +360,97 @@ def cleanmatslots(operator=None): objs = bpy.context.selected_editable_objects # collect all object names for warning_messages message_a = [] - # Flag if there are non MESH objects selected - mixed_obj = False + # Flags if there are non MESH objects selected + mixed_obj, mixed_obj_slot = False, False for ob in objs: - if ob.type == 'MESH': - mats = ob.material_slots.keys() - - # if mats is empty then mats[faceindex] will be out of range - if mats: - # check the polygons on the mesh to build a list of used materials - usedMatIndex = [] # we'll store used materials indices here - faceMats = [] - me = ob.data - for f in me.polygons: - # get the material index for this face... - faceindex = f.material_index - - # indices will be lost: Store face mat use by name - currentfacemat = mats[faceindex] - faceMats.append(currentfacemat) - - # check if index is already listed as used or not - found = False - for m in usedMatIndex: - if m == faceindex: - found = True - # break - - if found is False: - # add this index to the list - usedMatIndex.append(faceindex) - - # re-assign the used mats to the mesh and leave out the unused - ml = [] - mnames = [] - for u in usedMatIndex: - ml.append(mats[u]) - # we'll need a list of names to get the face indices... - mnames.append(mats[u]) - - assignmatslots(ob, ml) - - # restore face indices: - i = 0 - for f in me.polygons: - matindex = mnames.index(faceMats[i]) - f.material_index = matindex - i += 1 - else: - message_a.append(getattr(ob, "name", "NO NAME")) - continue - else: + if ob.type != 'MESH': + mat_empty = [] message_a.append(getattr(ob, "name", "NO NAME")) if mixed_obj is False: mixed_obj = True + + # at least try to remove empty material slots + if ob.type in {'CURVE', 'SURFACE', 'FONT', 'META'}: + mats = ob.material_slots + mat_empty = [i for i, slot in enumerate(mats) if not slot.material] + + if not mat_empty: + continue + + if mixed_obj_slot is False: + mixed_obj_slot = True + + # Ctx - copy the context for operator override + Ctx = bpy.context.copy() + # for this operator it needs only the active object replaced + Ctx['object'] = ob + + for index in mat_empty: + try: + ob.active_material_index = index + bpy.ops.object.material_slot_remove(Ctx) + except: + continue + continue + + mats = ob.material_slots.keys() + maxindex = len(mats) - 1 # indices start from zero + + # if mats is empty then mats[faceindex] will be out of range + if not mats: + message_a.append(getattr(ob, "name", "NO NAME")) continue + # check the polygons on the mesh to build a list of used materials + usedMatIndex = [] # we'll store used materials indices here + faceMats = [] + badIndices = set() # collect face indices that are out material slot's range + me = ob.data + for f in me.polygons: + # get the material index for this face... + faceindex = f.material_index + # check if the mats[faceindex] is not out of range + if faceindex > maxindex: + badIndices.add(faceindex) + continue + + # indices will be lost: Store face mat use by name + currentfacemat = mats[faceindex] + faceMats.append(currentfacemat) + + # check if index is already listed as used or not + found = False + for m in usedMatIndex: + if m == faceindex: + found = True + + if found is False: + # add this index to the list + usedMatIndex.append(faceindex) + + # re-assign the used mats to the mesh and leave out the unused + ml = [] + mnames = [] + for u in usedMatIndex: + if u not in badIndices: + ml.append(mats[u]) + # we'll need a list of names to get the face indices... + mnames.append(mats[u]) + + assignmatslots(ob, ml) + + # restore face indices: + i = 0 + for f in me.polygons: + if i not in badIndices: + matindex = mnames.index(faceMats[i]) + f.material_index = matindex + i += 1 + if message_a and operator: - warn_mess = ('C_OB_MIX_NO_MAT' if mixed_obj is True else 'C_OB_NO_MAT') + warn_s = 'C_OB_MIX_NO_MAT' if mixed_obj is True else 'C_OB_NO_MAT' + warn_mess = 'C_OB_MIX_SLOT_MAT' if mixed_obj_slot else warn_s warning_messages(operator, warn_mess, message_a) if actob: @@ -426,12 +466,11 @@ def cleanmatslots(operator=None): def assign_mat_mesh_edit(matname="Default", operator=None): actob = bpy.context.active_object found = False - for m in bpy.data.materials: - if m.name == matname: - target = m - found = True - break - if not found: + + # check if material exists, if it doesn't then create it + target = bpy.data.materials.get(matname) + + if not target: target = bpy.data.materials.new(matname) if (actob.type in {'MESH'} and actob.mode in {'EDIT'}): @@ -447,8 +486,8 @@ def assign_mat_mesh_edit(matname="Default", operator=None): break i += 1 + # the material is not attached to the object if not found: - # the material is not attached to the object actob.data.materials.append(target) # is selected ? @@ -472,7 +511,7 @@ def assign_mat_mesh_edit(matname="Default", operator=None): def assign_mat(matname="Default", operator=None): - # get active object so we can restore it later + # get the active object so we can restore it later actob = bpy.context.active_object # is active object selected ? @@ -480,19 +519,15 @@ def assign_mat(matname="Default", operator=None): actob.select = True # check if material exists, if it doesn't then create it - found = False - for m in bpy.data.materials: - if m.name == matname: - target = m - found = True - break + target = bpy.data.materials.get(matname) - if not found: + if not target: target = bpy.data.materials.new(matname) - # if objectmode then set all polygons + # if object mode then set all polygons editmode = False allpolygons = True + if actob.mode == 'EDIT': editmode = True allpolygons = False @@ -596,7 +631,7 @@ def check_texture(img, mat): def texface_to_mat(operator=None): - # editmode check here! + # edit mode check here! editmode = False ob = bpy.context.object if ob.mode == 'EDIT': @@ -604,7 +639,6 @@ def texface_to_mat(operator=None): bpy.ops.object.mode_set() for ob in bpy.context.selected_editable_objects: - faceindex = [] unique_images = [] # collect object names for warning messages @@ -668,27 +702,30 @@ def remove_materials(operator=None, setting="SLOT"): actob = bpy.context.active_object actob_name = getattr(actob, "name", "NO NAME") - if actob: - if not included_object_types(actob.type): - if operator: - warning_messages(operator, 'OB_CANT_MAT', actob_name) - else: - if (hasattr(actob.data, "materials") and - len(actob.data.materials) > 0): - if setting == "SLOT": - bpy.ops.object.material_slot_remove() - elif setting == "ALL": - for mat in actob.data.materials: - try: - bpy.ops.object.material_slot_remove() - except: - pass - - if operator: - warn_mess = ('R_ACT_MAT_ALL' if setting == "ALL" else 'R_ACT_MAT') - warning_messages(operator, warn_mess, actob_name) - elif operator: - warning_messages(operator, 'R_OB_NO_MAT', actob_name) + if not actob: + return + + if not included_object_types(actob.type): + if operator: + warning_messages(operator, 'OB_CANT_MAT', actob_name) + return + + if (hasattr(actob.data, "materials") and len(actob.data.materials) > 0): + if setting == "SLOT": + remove_material_slot() + elif setting == "ALL": + for mat in actob.data.materials: + try: + remove_material_slot() + except: + pass + + if operator: + warn_mess = 'R_ACT_MAT_ALL' if setting == "ALL" else 'R_ACT_MAT' + warning_messages(operator, warn_mess, actob_name) + + elif operator: + warning_messages(operator, 'R_OB_NO_MAT', actob_name) def remove_materials_all(operator=None): @@ -700,11 +737,10 @@ def remove_materials_all(operator=None): if not included_object_types(ob.type): continue else: - # code from blender stackexchange (by CoDEmanX) + # code from blender stack exchange (by CoDEmanX) ob.active_material_index = 0 - if (hasattr(ob.data, "materials") and - len(ob.material_slots) >= 1): + if (hasattr(ob.data, "materials") and len(ob.material_slots) >= 1): mat_count = True # Ctx - copy the context for operator override @@ -740,9 +776,10 @@ class VIEW3D_OT_show_mat_preview(Operator): @classmethod def poll(cls, context): - return (context.active_object is not None and - context.object.active_material is not None and - included_object_types(context.object.type)) + obj = context.active_object + return (obj is not None and + obj.active_material is not None and + included_object_types(obj.type)) def invoke(self, context, event): self.is_not_undo = True @@ -753,92 +790,107 @@ def draw(self, context): ob = context.active_object prw_size = size_preview() - if self.is_not_undo is True: - if ob and hasattr(ob, "active_material"): - mat = ob.active_material - is_opaque = (True if (ob and hasattr(ob, "show_transparent") and - ob.show_transparent is True) - else False) - is_opaque_bi = (True if (mat and hasattr(mat, "use_transparency") and - mat.use_transparency is True) - else False) - is_mesh = (True if ob.type == 'MESH' else False) - - if size_type_is_preview(): - layout.template_ID_preview(ob, "active_material", new="material.new", - rows=prw_size['Width'], cols=prw_size['Height']) - else: - layout.template_ID(ob, "active_material", new="material.new") - layout.separator() - - if c_render_engine("Both"): - layout.prop(mat, "use_nodes", icon='NODETREE') - - if c_need_of_viewport_colors(): - color_txt = ("Viewport Color:" if c_render_engine("Cycles") else "Diffuse") - spec_txt = ("Viewport Specular:" if c_render_engine("Cycles") else "Specular") - col = layout.column(align=True) - col.label(color_txt) - col.prop(mat, "diffuse_color", text="") - if c_render_engine("BI"): - # Blender Render - col.prop(mat, "diffuse_intensity", text="Intensity") - col.separator() - - col.label(spec_txt) - col.prop(mat, "specular_color", text="") - col.prop(mat, "specular_hardness") - - if (c_render_engine("BI") and not c_context_use_nodes()): - # Blender Render - col.separator() - col.prop(mat, "use_transparency") - col.separator() - if is_opaque_bi: - col.prop(mat, "transparency_method", text="") - col.separator() - col.prop(mat, "alpha") - elif (c_render_engine("Cycles") and is_mesh): - # Cycles - col.separator() - col.prop(ob, "show_transparent", text="Transparency") - if is_opaque: - col.separator() - col.prop(mat, "alpha") - col.separator() - col.label("Viewport Alpha:") - col.prop(mat.game_settings, "alpha_blend", text="") - layout.separator() - else: - other_render = ("*Unavailable with this Renderer*" if not c_render_engine("Both") - else "*Unavailable in this Context*") - no_col_label = ("*Only available in Solid Shading*" if c_render_engine("Cycles") - else other_render) - layout.label(no_col_label, icon="INFO") + if self.is_not_undo is False: + layout.label(text=UNDO_MESSAGE, icon="INFO") + return + + if not (ob and hasattr(ob, "active_material")): + layout.label(text="No Active Object or Active Material", icon="INFO") + return + + mat = ob.active_material + is_opaque = ( + True if (ob and hasattr(ob, "show_transparent") and + ob.show_transparent is True) else False + ) + is_opaque_bi = ( + True if (mat and hasattr(mat, "use_transparency") and + mat.use_transparency is True) else False + ) + is_mesh = True if ob.type == 'MESH' else False + + if size_type_is_preview(): + layout.template_ID_preview(ob, "active_material", new="material.new", + rows=prw_size['Width'], cols=prw_size['Height']) else: - layout.label(text="**Only Undo is available**", icon="INFO") + layout.template_ID(ob, "active_material", new="material.new") + layout.separator() + + if c_render_engine("Both"): + layout.prop(mat, "use_nodes", icon='NODETREE') + + if not c_need_of_viewport_colors(): + other_render = ( + "*Unavailable with this Renderer*" if not c_render_engine("Both") else + "*Unavailable in this Context*" + ) + no_col_label = ( + "*Only available in Solid Shading*" if c_render_engine("Cycles") else + other_render + ) + layout.label(no_col_label, icon="INFO") + return + + color_txt = "Viewport Color:" if c_render_engine("Cycles") else "Diffuse" + spec_txt = "Viewport Specular:" if c_render_engine("Cycles") else "Specular" + col = layout.column(align=True) + col.label(color_txt) + col.prop(mat, "diffuse_color", text="") + + if c_render_engine("BI"): + # Blender Render + col.prop(mat, "diffuse_intensity", text="Intensity") + col.separator() + + col.label(spec_txt) + col.prop(mat, "specular_color", text="") + col.prop(mat, "specular_hardness") + + if (c_render_engine("BI") and not c_context_use_nodes()): + # Blender Render + col.separator() + col.prop(mat, "use_transparency") + col.separator() + if is_opaque_bi: + col.prop(mat, "transparency_method", text="") + col.separator() + col.prop(mat, "alpha") + elif (c_render_engine("Cycles") and is_mesh): + # Cycles + col.separator() + col.prop(ob, "show_transparent", text="Transparency") + if is_opaque: + col.separator() + col.prop(mat, "alpha") + col.separator() + col.label("Viewport Alpha:") + col.prop(mat.game_settings, "alpha_blend", text="") + layout.separator() def check(self, context): return self.is_not_undo def execute(self, context): self.is_not_undo = False + return {'FINISHED'} class VIEW3D_OT_copy_material_to_selected(Operator): bl_idname = "view3d.copy_material_to_selected" bl_label = "Copy Materials to others" - bl_description = ("Copy Material From Active to Selected objects \n" + bl_description = ("Copy Material From Active to Selected objects\n" + "In case of multiple materials, only the first slot is assigned\n" "Works on Object's Data linked Materials") bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): + obj = context.active_object return (c_data_has_materials() and - context.active_object is not None and - included_object_types(context.active_object.type) and - context.object.active_material is not None and + obj is not None and + included_object_types(obj.type) and + obj.active_material is not None and context.selected_editable_objects) def execute(self, context): @@ -856,6 +908,7 @@ def execute(self, context): return {'CANCELLED'} warning_messages(self, warn_mess) + return {'FINISHED'} @@ -875,13 +928,14 @@ def invoke(self, context, event): return context.window_manager.invoke_confirm(self, event) def execute(self, context): - if context.selected_editable_objects: - texface_to_mat(self) - return {'FINISHED'} - else: + if not context.selected_editable_objects: warning_messages(self, 'TEX_MAT_NO_SL') return {'CANCELLED'} + texface_to_mat(self) + + return {'FINISHED'} + class VIEW3D_OT_set_new_material_name(Operator): bl_idname = "view3d.set_new_material_name" @@ -904,6 +958,7 @@ def draw(self, context): layout.prop(scene, "use_tweak") def execute(self, context): + return {'FINISHED'} @@ -911,34 +966,61 @@ class VIEW3D_OT_assign_material(Operator): bl_idname = "view3d.assign_material" bl_label = "Assign Material" bl_description = "Assign a material to the selection" + bl_property = "matname" bl_options = {'REGISTER', 'UNDO'} is_existing = BoolProperty( name="Is it a new Material", options={'HIDDEN'}, default=True, - ) + ) matname = StringProperty( name="Material Name", description="Name of the Material to Assign", options={'HIDDEN'}, default="Material_New", maxlen=128, - ) + ) + is_not_undo = False # prevent drawing props on undo @classmethod def poll(cls, context): return context.active_object is not None def invoke(self, context, event): - return self.execute(context) + self.is_not_undo = True + + if not self.is_existing or use_mat_menu_type() not in {'POPUP'}: + return self.execute(context) + + materials_lists_fill_names(context, refresh=True, is_object=False) + return context.window_manager.invoke_props_dialog(self, width=400, height=200) + + def check(self, context): + return self.is_not_undo + + def draw(self, context): + draw_ui_list_popups(self, context, obj_data=False) def execute(self, context): actob = context.active_object - mn = self.matname scene = context.scene.mat_specials + mn = self.matname tweak = scene.use_tweak + if use_mat_menu_type() == 'POPUP' and self.is_existing: + mats_col = context.scene.mat_specials_mats + len_mats = len(mats_col) + mat_index = scene.index_mat + + if not (len_mats > 0 and mat_index is not None and mat_index <= len_mats): + self.report({'WARNING'}, + "No Materials in the Scene. Please use the Add New option") + return {"CANCELLED"} + + mats_up = mats_col[mat_index].name + mn = mats_up + if not self.is_existing: new_name = check_mat_name_unique(scene.set_material_name) mn = new_name @@ -954,6 +1036,8 @@ def execute(self, context): mat_to_texface() self.is_not_undo = False + activate_mat_slot(actob, mn) + if tweak and not self.is_existing: try: bpy.ops.view3d.show_mat_preview('INVOKE_DEFAULT') @@ -979,6 +1063,7 @@ def poll(cls, context): def execute(self, context): cleanmatslots(self) + return {'FINISHED'} @@ -996,18 +1081,19 @@ def poll(cls, context): context.active_object is not None) def execute(self, context): - if context.selected_editable_objects: - mat_to_texface(self) - return {'FINISHED'} - else: + if not context.selected_editable_objects: warning_messages(self, "MAT_TEX_NO_SL") return {'CANCELLED'} + mat_to_texface(self) + + return {'FINISHED'} + class VIEW3D_OT_material_remove_slot(Operator): bl_idname = "view3d.material_remove_slot" bl_label = "Remove Active Slot (Active Object)" - bl_description = ("Remove active material slot from active object\n" + bl_description = ("Remove active material slot from active object \n" "Can't be used in Edit Mode") bl_options = {'REGISTER', 'UNDO'} @@ -1016,21 +1102,22 @@ def poll(cls, context): # materials can't be removed in Edit mode return (c_data_has_materials() and context.active_object is not None and - not context.object.mode == 'EDIT') + not context.active_object.mode == 'EDIT') def execute(self, context): - if context.selected_editable_objects: - remove_materials(self, "SLOT") - return {'FINISHED'} - else: + if not context.selected_editable_objects: warning_messages(self, 'R_NO_SL_MAT') return {'CANCELLED'} + remove_materials(self, "SLOT") + + return {'FINISHED'} + class VIEW3D_OT_material_remove_object(Operator): bl_idname = "view3d.material_remove_object" bl_label = "Remove all Slots (Active Object)" - bl_description = ("Remove all material slots from active object\n" + bl_description = ("Remove all material slots from active object \n" "Can't be used in Edit Mode") bl_options = {'REGISTER', 'UNDO'} @@ -1039,16 +1126,17 @@ def poll(cls, context): # materials can't be removed in Edit mode return (c_data_has_materials() and context.active_object is not None and - not context.object.mode == 'EDIT') + not context.active_object.mode == 'EDIT') def execute(self, context): - if context.selected_editable_objects: - remove_materials(self, "ALL") - return {'FINISHED'} - else: + if not context.selected_editable_objects: warning_messages(self, 'R_NO_SL_MAT') return {'CANCELLED'} + remove_materials(self, "ALL") + + return {'FINISHED'} + class VIEW3D_OT_material_remove_all(Operator): bl_idname = "view3d.material_remove_all" @@ -1062,19 +1150,20 @@ def poll(cls, context): # materials can't be removed in Edit mode return (c_data_has_materials() and context.active_object is not None and - not context.object.mode == 'EDIT') + not context.active_object.mode == 'EDIT') def invoke(self, context, event): return context.window_manager.invoke_confirm(self, event) def execute(self, context): - if context.selected_editable_objects: - remove_materials_all(self) - return {'FINISHED'} - else: + if not context.selected_editable_objects: warning_messages(self, 'R_NO_SL_MAT') return {'CANCELLED'} + remove_materials_all(self) + + return {'FINISHED'} + class VIEW3D_OT_select_material_by_name(Operator): bl_idname = "view3d.select_material_by_name" @@ -1083,20 +1172,59 @@ class VIEW3D_OT_select_material_by_name(Operator): bl_options = {'REGISTER', 'UNDO'} matname = StringProperty( - name='Material Name', - description='Name of Material to Select', - maxlen=63, - ) + name="Material Name", + description="Name of Material to Select", + maxlen=63, + ) + is_not_undo = False + is_edit = False @classmethod def poll(cls, context): + obj = context.active_object return (c_data_has_materials() and - context.active_object is not None) + obj is not None and obj.mode in {"OBJECT", "EDIT"}) + + def invoke(self, context, event): + self.is_not_undo = True + + if use_mat_menu_type() not in {'POPUP'}: + return self.execute(context) + + obj = context.active_object + self.is_edit = bool(obj.mode == 'EDIT') + materials_lists_fill_names(context, refresh=True, is_object=self.is_edit) + + return context.window_manager.invoke_props_dialog(self, width=400, height=200) + + def check(self, context): + return self.is_not_undo + + def draw(self, context): + draw_ui_list_popups(self, context, obj_data=self.is_edit) def execute(self, context): - mn = self.matname + if use_mat_menu_type() == 'POPUP': + mats_col = context.scene.mat_specials_mats + scene = context.scene.mat_specials + len_mats = len(mats_col) + mat_index = scene.index_mat + + if not (len_mats > 0 and mat_index is not None and mat_index <= len_mats): + self.report({'WARNING'}, + "No materials found. Operation Cancelled") + return {"CANCELLED"} + + mats_up = mats_col[mat_index].name + mn = mats_up + else: + mn = self.matname + select_material_by_name(mn) - warning_messages(self, 'SL_MAT_BY_NAME', mn) + message = 'SL_MAT_EDIT_BY_NAME' if self.is_edit else 'SL_MAT_BY_NAME' + warning_messages(self, message, mn) + self.is_not_undo = False + return {'FINISHED'} @@ -1107,25 +1235,26 @@ class VIEW3D_OT_replace_material(Operator): bl_options = {'REGISTER', 'UNDO'} matorg = StringProperty( - name="Original", - description="Material to replace", - maxlen=63, - ) + name="Original", + description="Material to replace", + maxlen=63, + ) matrep = StringProperty( - name="Replacement", - description="Replacement material", - maxlen=63, - ) + name="Replacement", + description="Replacement material", + maxlen=63, + ) all_objects = BoolProperty( - name="All objects", - description="Replace for all objects in this blend file", - default=True, - ) + name="All objects", + description="If enabled, replace for all objects in this blend file\n" + "If disabled, only selected objects will be affected", + default=False, + ) update_selection = BoolProperty( - name="Update Selection", - description="Select affected objects and deselect unaffected", - default=True, - ) + name="Update Selection", + description="Select affected objects and deselect unaffected", + default=True, + ) @classmethod def poll(cls, context): @@ -1142,9 +1271,12 @@ def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self) def execute(self, context): - replace_material(self.matorg, self.matrep, self.all_objects, - self.update_selection, self) + replace_material( + self.matorg, self.matrep, self.all_objects, + self.update_selection, self + ) self.matorg, self.matrep = "", "" + return {'FINISHED'} @@ -1155,22 +1287,22 @@ class VIEW3D_OT_fake_user_set(Operator): bl_options = {'REGISTER', 'UNDO'} fake_user = EnumProperty( - name="Fake User", - description="Turn fake user on or off", - items=(('ON', "On", "Enable fake user"), ('OFF', "Off", "Disable fake user")), - default='ON', - ) + name="Fake User", + description="Turn fake user on or off", + items=(('ON', "On", "Enable fake user"), ('OFF', "Off", "Disable fake user")), + default='ON', + ) materials = EnumProperty( - name="Materials", - description="Chose what objects and materials to affect", - items=(('ACTIVE', "Active object", "Materials of active object only"), - ('SELECTED', "Selected objects", "Materials of selected objects"), - ('SCENE', "Scene objects", "Materials of objects in current scene"), - ('USED', "Used", "All materials used by objects"), - ('UNUSED', "Unused", "Currently unused materials"), - ('ALL', "All", "All materials in this blend file")), - default='UNUSED', - ) + name="Materials", + description="Chose what objects and materials to affect", + items=(('ACTIVE', "Active object", "Materials of active object only"), + ('SELECTED', "Selected objects", "Materials of selected objects"), + ('SCENE', "Scene objects", "Materials of objects in current scene"), + ('USED', "Used", "All materials used by objects"), + ('UNUSED', "Unused", "Currently unused materials"), + ('ALL', "All", "All materials in this blend file")), + default='UNUSED', + ) @classmethod def poll(cls, context): @@ -1186,6 +1318,7 @@ def invoke(self, context, event): def execute(self, context): fake_user_set(self.fake_user, self.materials, self) + return {'FINISHED'} @@ -1199,15 +1332,15 @@ class MATERIAL_OT_set_transparent_back_side(Operator): @classmethod def poll(cls, context): obj = context.active_object - if (not obj): + if not obj: return False mat = obj.active_material - if (not mat): + if not mat: return False - if (mat.node_tree): + if mat.node_tree: if (len(mat.node_tree.nodes) == 0): return True - if (not mat.use_nodes): + if not mat.use_nodes: return True return False @@ -1249,7 +1382,7 @@ class MATERIAL_OT_move_slot_top(Operator): @classmethod def poll(cls, context): obj = context.active_object - if (not obj): + if not obj: return False if (len(obj.material_slots) <= 2): return False @@ -1279,7 +1412,7 @@ class MATERIAL_OT_move_slot_bottom(Operator): @classmethod def poll(cls, context): obj = context.active_object - if (not obj): + if not obj: return False if (len(obj.material_slots) <= 2): return False @@ -1309,15 +1442,14 @@ class MATERIAL_OT_link_to_base_names(Operator): bl_options = {'REGISTER', 'UNDO'} mat_keep = StringProperty( - name="Material to keep", - default="", - ) + name="Material to keep", + default="", + ) is_auto = BoolProperty( - name="Auto Rename/Replace", - description=("Automatically Replace names " - "by stripping numerical suffix"), - default=False, - ) + name="Auto Rename/Replace", + description="Automatically Replace names by stripping numerical suffix", + default=False, + ) mat_error = [] # collect mat for warning messages is_not_undo = False # prevent drawing props on undo check_no_name = True # check if no name is passed @@ -1328,16 +1460,18 @@ def poll(cls, context): def draw(self, context): layout = self.layout - if self.is_not_undo is True: - boxee = layout.box() - boxee.prop_search(self, "mat_keep", bpy.data, "materials") - boxee.enabled = not self.is_auto - layout.separator() - - boxs = layout.box() - boxs.prop(self, "is_auto", text="Auto Rename/Replace", icon="SYNTAX_ON") - else: - layout.label(text="**Only Undo is available**", icon="INFO") + + if self.is_not_undo is False: + layout.label(text=UNDO_MESSAGE, icon="INFO") + return + + boxee = layout.box() + boxee.prop_search(self, "mat_keep", bpy.data, "materials") + boxee.enabled = not self.is_auto + layout.separator() + + boxs = layout.box() + boxs.prop(self, "is_auto", text="Auto Rename/Replace", icon="SYNTAX_ON") def invoke(self, context, event): self.is_not_undo = True @@ -1433,13 +1567,14 @@ def execute(self, context): warning_messages(self, 'MAT_LINK_ERROR', self.mat_error, 'MAT') self.is_not_undo = False + return {'FINISHED'} class MATERIAL_OT_check_converter_path(Operator): bl_idname = "material.check_converter_path" bl_label = "Check Converters images/data save path" - bl_description = "Check if the given path is writeable (has OS writing privileges)" + bl_description = "Check if the given path is writable (has OS writing privileges)" bl_options = {'REGISTER', 'INTERNAL'} def check_valid_path(self, context): @@ -1450,24 +1585,24 @@ def check_valid_path(self, context): warning_messages(self, "DIR_PATH_EMPTY", override=True) return False - if os_path.exists(paths): - if os_access(paths, os.W_OK | os.X_OK): - try: - path_test = os_path.join(paths, "XYfoobartestXY.txt") - with open(path_test, 'w') as f: - f.closed - os_remove(path_test) - return True - except (OSError, IOError): - warning_messages(self, 'DIR_PATH_W_ERROR', override=True) - return False - else: - warning_messages(self, 'DIR_PATH_A_ERROR', override=True) - return False - else: + if not os_path.exists(paths): warning_messages(self, 'DIR_PATH_N_ERROR', override=True) return False + if not os_access(paths, os.W_OK | os.X_OK): + warning_messages(self, 'DIR_PATH_A_ERROR', override=True) + return False + + try: + path_test = os_path.join(paths, "XYfoobartestXY.txt") + with open(path_test, 'w') as f: + f.closed + os_remove(path_test) + return True + except (OSError, IOError): + warning_messages(self, 'DIR_PATH_W_ERROR', override=True) + return False + return True def execute(self, context): @@ -1479,6 +1614,67 @@ def execute(self, context): return {'FINISHED'} +# Material selections pop-up + +class VIEW3D_UL_assign_material_popup_ui(UIList): + + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + self.use_filter_show = True + + col = layout.column(align=True) + col.alignment = "LEFT" + mat = bpy.data.materials.get(item.name, None) + if not mat: + col.label(text="{} - is not available".format(item.name), icon="ERROR") + else: + split = col.split(percentage=0.75, align=True) + row = split.row(align=True) + row.label(text=item.name, translate=False, icon="MATERIAL_DATA") + subrow = split.row(align=True) + subrow.alignment = "RIGHT" + subrow.label(text="", icon="PINNED" if item.mat_fake_user else "BLANK1") + subrow = split.row(align=True) + subrow.alignment = "RIGHT" + subrow.label(text="", icon="LINK_BLEND" if item.mat_lib else "BLANK1") + + +def draw_ui_list_popups(self, context, obj_data=False): + layout = self.layout + + if not self.is_not_undo: + layout.label(text=UNDO_MESSAGE, icon="INFO") + return + + matlen = len(context.scene.mat_specials_mats) + matdata = "in Object's Data" if obj_data else "in Data" + matgramma = "Material" if matlen == 1 else "Materials" + matcount = "No" if matlen < 1 else matlen + + box = layout.box() + col = box.column(align=True) + col.label(text="{} {} {}".format(matcount, matgramma, matdata), + icon="INFO") + sub_split = col.split(percentage=0.7, align=True) + sub_box_1 = sub_split.box() + sub_box_1.label("Name") + sub_split_2 = sub_split.split(percentage=0.5, align=True) + sub_box_2 = sub_split_2.box() + sub_box_2.label("Fake") + sub_box_3 = sub_split_2.box() + sub_box_3.label("Lib") + + col.template_list( + "VIEW3D_UL_assign_material_popup_ui", + 'mat_specials', + context.scene, + 'mat_specials_mats', + context.scene.mat_specials, + 'index_mat', + rows=10 + ) + return + + # Menu classes class VIEW3D_MT_assign_material(Menu): @@ -1488,26 +1684,45 @@ def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - if c_data_has_materials(): - # no materials - for material_name in bpy.data.materials.keys(): - mats = layout.operator("view3d.assign_material", - text=material_name, - icon='MATERIAL_DATA') - mats.matname = material_name - mats.is_existing = True - use_separator(self, context) - if (not context.active_object): # info why the add material is innactive layout.label(text="*No active Object in the Scene*", icon="INFO") use_separator(self, context) + mat_prop_name = context.scene.mat_specials.set_material_name - add_new = layout.operator("view3d.assign_material", - text="Add New", icon='ZOOMIN') + add_new = layout.operator( + "view3d.assign_material", + text="Add New", icon='ZOOMIN' + ) add_new.matname = mat_prop_name add_new.is_existing = False + if use_mat_menu_type() != 'POPUP': + use_separator(self, context) + + if c_data_has_materials(): + mat_entry = layout.column() + + if use_mat_menu_type() == 'POPUP': + matp = mat_entry.operator( + "view3d.assign_material", + icon='MATERIAL_DATA' + ) + matp.is_existing = True + elif use_mat_menu_type() == 'COLUMNS': + get_col_size = switch_to_column() + mat_entry = layout.column_flow(columns=get_col_size) + + if use_mat_menu_type() in {'COLUMNS', 'STANDARD'}: + for material_name in bpy.data.materials.keys(): + mats = mat_entry.operator( + "view3d.assign_material", + text=material_name, + icon='MATERIAL_DATA' + ) + mats.matname = material_name + mats.is_existing = True + class VIEW3D_MT_select_material(Menu): bl_label = "Select by Material" @@ -1515,28 +1730,58 @@ class VIEW3D_MT_select_material(Menu): def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - ob = context.object + obj = context.active_object + has_users = False + col = layout.column() + + if not c_data_has_materials(): + layout.label(text="*No Materials in the Data*", icon="INFO") + return + elif not obj: + layout.label(text="*No Active Object*", icon="INFO") + return + elif obj.mode == "EDIT" and not c_obj_data_has_materials(obj): + layout.label(text="*No Materials in the Object's Data*", icon="INFO") + return + elif use_mat_menu_type() == 'POPUP': + col.operator( + "view3d.select_material_by_name", + text="Select by Material", + icon='HAND', + ) + return - if (not c_data_has_materials()): - layout.label(text="*No Materials in the data*", icon="INFO") - elif (not ob): - layout.label(text="*No Objects to select*", icon="INFO") + if obj.mode == 'OBJECT': + if use_mat_menu_type() == 'COLUMNS': + get_col_size = switch_to_column(is_edit=False) + col = layout.column_flow(columns=get_col_size) + # show all used materials in entire blend file + for material_name, material in bpy.data.materials.items(): + if material.users > 0: + has_users = True + col.operator( + "view3d.select_material_by_name", + text=material_name, + icon='MATERIAL_DATA', + ).matname = material_name + if not has_users: + layout.label(text="*No users for Materials in the data*", icon="INFO") + return + elif obj.mode == 'EDIT': + if use_mat_menu_type() == 'COLUMNS': + get_col_size = switch_to_column(is_edit=True) + col = layout.column_flow(columns=get_col_size) + # show only the materials on this object + mats = obj.material_slots.keys() + for m in mats: + if m not in "": # skip empty slots + col.operator( + "view3d.select_material_by_name", + text=m, + icon='MATERIAL_DATA' + ).matname = m else: - if ob.mode == 'OBJECT': - # show all used materials in entire blend file - for material_name, material in bpy.data.materials.items(): - if (material.users > 0): - layout.operator("view3d.select_material_by_name", - text=material_name, - icon='MATERIAL_DATA', - ).matname = material_name - elif ob.mode == 'EDIT': - # show only the materials on this object - mats = ob.material_slots.keys() - for m in mats: - layout.operator("view3d.select_material_by_name", - text=m, - icon='MATERIAL_DATA').matname = m + layout.label(text="*Works only in Object and Edit mode*", icon="INFO") class VIEW3D_MT_remove_material(Menu): @@ -1546,24 +1791,35 @@ def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("view3d.clean_material_slots", - text="Clean Material Slots", - icon='COLOR_BLUE') - use_separator(self, context) + if context.mode in {'PAINT_TEXTURE'}: + layout.label( + text="Removing materials can lead to loss of painting data", + icon="INFO" + ) + use_separator(self, context) - if not c_render_engine("Lux"): - layout.operator("view3d.material_remove_slot", icon='COLOR_GREEN') - layout.operator("view3d.material_remove_object", icon='COLOR_RED') + layout.operator( + "view3d.clean_material_slots", + text="Clean Material Slots", + icon='COLOR_BLUE' + ) + use_separator(self, context) - if use_remove_mat_all(): - use_separator(self, context) - layout.operator("view3d.material_remove_all", - text="Remove Material Slots " - "(All Selected Objects)", - icon='CANCEL') - else: + if c_render_engine("Lux"): layout.label(text="Sorry, other Menu functions are", icon="INFO") layout.label(text="unvailable with Lux Renderer") + return + + layout.operator("view3d.material_remove_slot", icon='COLOR_GREEN') + layout.operator("view3d.material_remove_object", icon='COLOR_RED') + + if use_remove_mat_all(): + use_separator(self, context) + layout.operator( + "view3d.material_remove_all", + text="Remove Material Slots (All Selected Objects)", + icon='CANCEL' + ) class VIEW3D_MT_master_material(Menu): @@ -1577,8 +1833,13 @@ def draw(self, context): layout.operator("view3d.show_mat_preview", icon="VISIBLE_IPO_ON") use_separator(self, context) - layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN') - layout.menu("VIEW3D_MT_select_material", icon='HAND') + if use_mat_menu_type() == 'POPUP': + VIEW3D_MT_assign_material.draw(self, context) + use_separator(self, context) + VIEW3D_MT_select_material.draw(self, context) + else: + layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN') + layout.menu("VIEW3D_MT_select_material", icon='HAND') use_separator(self, context) layout.operator("view3d.copy_material_to_selected", icon="COPY_ID") @@ -1668,8 +1929,15 @@ def menu_func(self, context): layout.operator_context = 'INVOKE_REGION_WIN' use_separator(self, context) - layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN') - layout.menu("VIEW3D_MT_select_material", icon='HAND') + if use_mat_menu_type() == 'POPUP': + VIEW3D_MT_assign_material.draw(self, context) + use_separator(self, context) + VIEW3D_MT_select_material.draw(self, context) + else: + layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN') + layout.menu("VIEW3D_MT_select_material", icon='HAND') + use_separator(self, context) + layout.operator("view3d.replace_material", text='Replace Material', icon='ARROW_LEFTRIGHT') @@ -1744,11 +2012,15 @@ class MATERIAL_PT_scenemassive(Panel): def poll(cls, context): return (enable_converters() is True and converter_type('BI_CONV')) + def draw_header(self, context): + layout = self.layout + help_panel_header(layout, menu_type="HELP_MT_biconvert") + def draw(self, context): layout = self.layout sc = context.scene - row = layout.row() - box = row.box() + col = layout.column(align=True) + box = col.box() split = box.box().split(0.5) split.operator("ml.refresh", @@ -1760,14 +2032,9 @@ def draw(self, context): icon='MATERIAL') ml_restore.switcher = False ml_restore.renderer = "BI" - - box = layout.box() - row = box.row() row.menu("scenemassive.opt", text="Advanced Options", icon='SCRIPTWIN') - row.menu("help.biconvert", - text="Usage Information Guide", icon="INFO") - box = layout.box() + box = col.box() box.label("Save Directory") split = box.split(0.85) split.prop(sc.mat_specials, "conv_path", text="", icon="RENDER_RESULT") @@ -1786,33 +2053,37 @@ class MATERIAL_PT_xps_convert(Panel): def poll(cls, context): return (enable_converters() is True and converter_type('CYC_CONV')) + def draw_header(self, context): + layout = self.layout + help_panel_header(layout, menu_type="help.nodeconvert") + def draw(self, context): layout = self.layout - row = layout.row() - box = row.box() + col = layout.column(align=True) + box = col.box() - box.label(text="Multi Image Support (Imports)") + box.label(text="Multi Image Support (Imports)", icon="INFO") split = box.box().split(0.5) - split.operator("xps_tools.convert_to_cycles_all", - text="Convert All to Nodes", icon="TEXTURE") - split.operator("xps_tools.convert_to_cycles_selected", - text="Convert Selected to Nodes", icon="TEXTURE") + split.operator( + "xps_tools.convert_to_cycles_all", + text="Convert All to Nodes", icon="TEXTURE" + ) + split.operator( + "xps_tools.convert_to_cycles_selected", + text="Convert Selected to Nodes", icon="TEXTURE" + ) - box = layout.box() - row = box.row() - ml_restore = row.operator("ml.restore", text="To BI Nodes ON", + box = col.box() + ml_restore = box.operator("ml.restore", text="To BI Nodes ON", icon='MATERIAL') ml_restore.switcher = True ml_restore.renderer = "BI" - row.menu("help.nodeconvert", - text="Usage Information Guide", icon="INFO") - # Converters Help # class MATERIAL_MT_biconv_help(Menu): - bl_idname = "help.biconvert" + bl_idname = "HELP_MT_biconvert" bl_description = "Read Instructions & Current Limitations" bl_label = "Usage Information Guide" bl_options = {'REGISTER'} @@ -1829,12 +2100,12 @@ def draw(self, context): layout.label(text="Select the texture loaded in the image node") layout.label(text="Press Ctrl/T to create the image nodes") layout.label(text="In the Node Editor, Select the Diffuse Node") - layout.label(text="Enable Node Wrangler addon", icon="NODETREE") + layout.label(text="Enable Node Wrangler add-on", icon="NODETREE") layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE") use_separator(self, context) layout.label(text="Extract Alpha: the images have to have alpha channel") layout.label(text="The default path is the folder where the current .blend is") - layout.label(text="During Baking, the script will check writting privileges") + layout.label(text="During Baking, the script will check writing privileges") layout.label(text="Set the save path for extracting images with full access") layout.label(text="May Require Run As Administrator on Windows OS", icon="ERROR") layout.label(text="Converts Bi Textures to Image Files:", icon="MOD_EXPLODE") @@ -1864,7 +2135,7 @@ def draw(self, context): layout.label(text="Select the texture loaded in the image node") layout.label(text="Press Ctrl/T to create the image nodes") layout.label(text="In the Node Editor, Select the Diffuse Node") - layout.label(text="Enable Node Wrangler addon", icon="NODETREE") + layout.label(text="Enable Node Wrangler add-on", icon="NODETREE") layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE") use_separator(self, context) layout.label(text="For Specular Nodes, Image color influence has to be enabled") @@ -1872,7 +2143,7 @@ def draw(self, context): layout.label(text="The Converter report can point out to some failures") layout.label(text="Not all Files will produce good results", icon="ERROR") layout.label(text="fbx, .dae, .obj, .3ds, .xna and more") - layout.label(text="**Supports Imported Files**:", icon="IMPORT") + layout.label(text="*Supports Imported Files*:", icon="IMPORT") use_separator(self, context) layout.label(text="For some file types") layout.label(text="Supports Alpha, Normals, Specular and Diffuse") @@ -1883,7 +2154,7 @@ def draw(self, context): # Make Report -class material_converter_report(Operator): +class MATERIAL_OT_converter_report(Operator): bl_idname = "mat_converter.reports" bl_label = "Material Converter Report" bl_description = "Report about done Material Conversions" @@ -1904,208 +2175,309 @@ def invoke(self, context, event): return context.window_manager.invoke_props_dialog(self, width=500) def execute(self, context): + return {'FINISHED'} # Scene Properties +class material_specials_scene_mats(PropertyGroup): + name = StringProperty() + mat_lib = BoolProperty( + default=False + ) + mat_fake_user = BoolProperty( + default=False + ) + + class material_specials_scene_props(PropertyGroup): conv_path = StringProperty( - name="Save Directory", - description=("Path to save images during conversion \n" - "Default is the location of the blend file"), - default="//", - subtype='DIR_PATH', - ) + name="Save Directory", + description="Path to save images during conversion\n" + "Default is the location of the blend file", + default="//", + subtype='DIR_PATH' + ) EXTRACT_ALPHA = BoolProperty( - attr="EXTRACT_ALPHA", - default=False, - description=("Extract Alpha channel from non-procedural images \n" - "Don't use this option if the image doesn't have Alpha"), - ) + attr="EXTRACT_ALPHA", + default=False, + description="Extract Alpha channel from non-procedural images\n" + "Don't use this option if the image doesn't have Alpha" + ) SET_FAKE_USER = BoolProperty( - attr="SET_FAKE_USER", - default=False, - description="Set fake user on unused images, so they can be kept in the .blend", - ) + attr="SET_FAKE_USER", + default=False, + description="Set fake user on unused images, so they can be kept in the .blend" + ) EXTRACT_PTEX = BoolProperty( - attr="EXTRACT_PTEX", - default=False, - description="Extract procedural images and bake them to jpeg", - ) + attr="EXTRACT_PTEX", + default=False, + description="Extract procedural images and bake them to jpeg" + ) EXTRACT_OW = BoolProperty( - attr="Overwrite", - default=False, - description="Extract textures again instead of re-using priorly extracted textures", - ) + attr="Overwrite", + default=False, + description="Extract textures again instead of re-using previously extracted textures" + ) SCULPT_PAINT = BoolProperty( - attr="SCULPT_PAINT", - default=False, - description=("Conversion geared towards sculpting and painting.\n" - "Creates a diffuse, glossy mixed with layer weight. \n" - "Image nodes are not connected"), - ) + attr="SCULPT_PAINT", + default=False, + description="Conversion geared towards sculpting and painting.\n" + "Creates a diffuse, glossy mixed with layer weight.\n" + "Image nodes are not connected" + ) UV_UNWRAP = BoolProperty( - attr="UV_UNWRAP", - default=False, - description=("Use automatical Angle based UV Unwrap of the active Object"), - ) + attr="UV_UNWRAP", + default=False, + description="Use automatic Angle based UV Unwrap for the Active Object" + ) enable_report = BoolProperty( - attr="enable_report", - default=False, - description=("Enable Converter Report in the UI"), - ) + attr="enable_report", + default=False, + description="Enable Converter Report in the UI" + ) img_bake_size = EnumProperty( - name="Bake Image Size", - description="Set the resolution size of baked images \n", - items=(('512', "Set : 512 x 512", "Bake Resolution 512 x 512"), - ('1024', "Set : 1024 x 1024", "Bake Resolution 1024 x 1024"), - ('2048', "Set : 2048 x 2048", "Bake Resolution 2048 x 2048")), - default='1024', - ) + name="Bake Image Size", + description="Set the resolution size of baked images \n", + items=( + ('512', "Set : 512 x 512", "Bake Resolution 512 x 512"), + ('1024', "Set : 1024 x 1024", "Bake Resolution 1024 x 1024"), + ('2048', "Set : 2048 x 2048", "Bake Resolution 2048 x 2048") + ), + default='1024' + ) set_material_name = StringProperty( - name="New Material name", - description="What Base name pattern to use for a new created Material\n" - "It is appended by an automatic numeric pattern depending\n" - "on the number of Scene's materials containing the Base", - default="Material_New", - maxlen=128, - ) + name="New Material name", + description="What Base name pattern to use for a new created Material\n" + "It is appended by an automatic numeric pattern depending\n" + "on the number of Scene's materials containing the Base", + default="Material_New", + maxlen=128 + ) use_tweak = BoolProperty( name="Tweak Settings", description="Open Preview Active Material after new Material creation", - default=False, - ) + default=False + ) + index_mat = IntProperty( + name="index", + options={"HIDDEN"} + ) -# Addon Preferences +# Add-on Preferences class VIEW3D_MT_material_utils_pref(AddonPreferences): bl_idname = __name__ show_warnings = BoolProperty( - name="Enable Warning messages", - default=False, - description="Show warning messages \n" - "when an action is executed or failed.\n \n" - "Advisable if you don't know how the tool works", - ) + name="Enable Warning messages", + default=False, + description="Show warning messages when an action is executed or failed" + ) show_remove_mat = BoolProperty( - name="Enable Remove all Materials", - default=False, - description="Enable Remove all Materials for all Selected Objects\n\n" - "Use with care - if you want to keep materials after\n" - "closing or reloading Blender, Set Fake User for them", - ) + name="Enable Remove all Materials", + default=False, + description="Enable Remove all Materials for all Selected Objects\n\n" + "Use with care - if you want to keep materials after\n" + "closing or reloading Blender, Set Fake User for them" + ) show_mat_preview = BoolProperty( - name="Enable Material Preview", - default=True, - description="Material Preview of the Active Object \n" - "Contains the preview of the active Material, \n" - "Use nodes, Color, Specular and Transparency \n" - "settings depending on the Context and Preferences", - ) + name="Enable Material Preview", + default=True, + description="Material Preview of the Active Object\n" + "Contains the preview of the active Material,\n" + "Use nodes, Color, Specular and Transparency\n" + "settings depending on the Context and Preferences" + ) set_cleanmatslots = BoolProperty( - name="Enable Auto Clean", - default=True, - description="Enable Automatic Removal of unused Material Slots \n" - "called together with the Assign Material menu option. \n \n" - "Apart from preference and the cases when it affects \n" - "adding materials, enabling it can have some \n" - "performance impact on very dense meshes", - ) + name="Enable Auto Clean", + default=True, + description="Enable Automatic Removal of unused Material Slots\n" + "called together with the Assign Material menu option.\n" + "Apart from preference and the cases when it affects\n" + "adding materials, enabling it can have some\n" + "performance impact on very dense meshes" + ) show_separators = BoolProperty( - name="Use Separators in the menus", - default=True, - description="Use separators in the menus, a trade-off between \n" - "readability vs. using more space for displaying items", - ) + name="Use Separators in the menus", + default=True, + description="Use separators in the menus, a trade-off between\n" + "readability vs. using more space for displaying items" + ) show_converters = BoolProperty( - name="Enable Converters", - default=False, - description="Enable Material Converters", - ) + name="Enable Converters", + default=False, + description="Enable Material Converters" + ) set_preview_size = EnumProperty( - name="Preview Menu Size", - description="Set the preview menu size \n" - "depending on the number of materials \n" - "in the scene (width and height)", - items=(('2x2', "Size 2x2", "Width 2 Height 2"), - ('2x3', "Size 2x3", "Width 3 Height 2"), - ('3x3', "Size 3x3", "Width 3 Height 3"), - ('3x4', "Size 3x4", "Width 4 Height 3"), - ('4x4', "Size 4x4", "Width 4 Height 4"), - ('5x5', "Size 5x5", "Width 5 Height 5"), - ('6x6', "Size 6x6", "Width 6 Height 6"), - ('0x0', "List", "Display as a List")), - default='3x3', - ) + name="Preview Menu Size", + description="Set the preview menu size\n" + "depending on the number of materials\n" + "in the scene (width and height)", + items=( + ('2x2', "Size 2x2", "Width 2 Height 2"), + ('2x3', "Size 2x3", "Width 3 Height 2"), + ('3x3', "Size 3x3", "Width 3 Height 3"), + ('3x4', "Size 3x4", "Width 4 Height 3"), + ('4x4', "Size 4x4", "Width 4 Height 4"), + ('5x5', "Size 5x5", "Width 5 Height 5"), + ('6x6', "Size 6x6", "Width 6 Height 6"), + ('0x0', "List", "Display as a List") + ), + default='3x3' + ) set_preview_type = EnumProperty( - name="Preview Menu Type", - description="Set the the Preview menu type", - items=(('LIST', "Classic", - "Display as a Classic List like in Blender Propreties.\n" - "Preview of Active Material is not available"), - ('PREVIEW', "Preview Display", - "Display as a preview of Thumbnails\n" - "It can have some performance issues with scenes containing a lot of materials\n" - "Preview of Active Material is available")), - default='PREVIEW', - ) + name="Preview Menu Type", + description="Set the the Preview menu type", + items=( + ('LIST', "Classic", + "Display as a Classic List like in Blender Properties.\n" + "Preview of Active Material is not available\n\n" + "Note: Choosing a different material from the list will replace the active one"), + ('PREVIEW', "Preview Display", + "Display as a preview of Thumbnails\n" + "It can have some performance issues with scenes containing a lot of materials\n" + "Preview of Active Material is available\n\n" + "Note: Choosing a different material from the list will replace the active one") + ), + default='PREVIEW' + ) set_experimental_type = EnumProperty( - name="Experimental Features", - description="Set the Type of converters enabled", - items=(('ALL', "All Converters", - "Enable all Converters"), - ('CYC_CONV', "BI and Cycles Nodes", - "Enable Cycles related Convert"), - ('BI_CONV', "BI To Cycles", - "Enable Blender Internal related Converters")), - default='ALL', - ) + name="Experimental Features", + description="Set the type of converters enabled", + items=( + ('ALL', "All Converters", + "Enable all Converters"), + ('CYC_CONV', "BI and Cycles Nodes", + "Enable Cycles related Convert"), + ('BI_CONV', "BI To Cycles", + "Enable Blender Internal related Converters") + ), + default='ALL', + ) + set_add_material_menu = EnumProperty( + name="Add Material Menu", + description="Set the type of Add Material menu", + items=( + ('STANDARD', "Standard Menu", + "Material entries in the menu are bellow each other"), + ('COLUMNS', "Column Menu", + "Material entries are placed in column blocks"), + ('POPUP', "Pop up Menu", + "Material entries are placed in a scrollable list inside a pop-up menu") + ), + default='POPUP' + ) def draw(self, context): layout = self.layout sc = context.scene - box = layout.box() + col_m = layout.column(align=True) + + box = col_m.box() box.label("Save Directory") split = box.split(0.85) split.prop(sc.mat_specials, "conv_path", text="", icon="RENDER_RESULT") - split.operator("material.check_converter_path", - text="", icon="EXTERNAL_DATA") - - box = layout.box() + split.operator( + "material.check_converter_path", + text="", icon="EXTERNAL_DATA" + ) + box = col_m.box() split = box.split(align=True) - col = split.column() + col = split.column(align=True) col.prop(self, "show_warnings") col.prop(self, "show_remove_mat") - - col = split.column() - col.alignment = 'RIGHT' col.prop(self, "set_cleanmatslots") col.prop(self, "show_separators") - boxie = box.box() - split = boxie.split(percentage=0.3) + col = split.column(align=True) + col.label("Apply / Select Material mode:") + col.prop(self, "set_add_material_menu", expand=True) + + box = col_m.box() + size_split = 0.3 if self.show_mat_preview else 1.0 + split = box.split(percentage=size_split, align=True) split.prop(self, "show_mat_preview") + if self.show_mat_preview: - rowsy = split.row(align=True) - rowsy.enabled = True if self.show_mat_preview else False - rowsy.prop(self, "set_preview_type", expand=True) - rowsa = boxie.row(align=True) - rowsa.enabled = True if self.set_preview_type in {'PREVIEW'} else False - rowsa.prop(self, "set_preview_size", expand=True) - - boxif = box.box() - split = boxif.split(percentage=0.3) + subsplit = split.split(percentage=0.7, align=True) + row = subsplit.row(align=True) + row.prop(self, "set_preview_type", expand=True) + + subrow = subsplit.row(align=True) + subrow.enabled = True if self.set_preview_type in {'PREVIEW'} else False + subrow.prop(self, "set_preview_size", text="") + + box = col_m.box() + size_split = 0.3 if self.show_converters else 1.0 + split = box.split(percentage=size_split, align=True) split.prop(self, "show_converters") + if self.show_converters: - rowe = split.row(align=True) - rowe.prop(self, "set_experimental_type", expand=True) + row = split.row(align=True) + row.prop(self, "set_experimental_type", expand=True) # utility functions: +def help_panel_header(layout, menu_type="VIEW3D_MT_master_material"): + layout.separator() + box = layout.box() + box.scale_y = 0.5 + box.menu(menu_type, text="", icon="INFO") + layout.separator() + + +def activate_mat_slot(actob, matname): + mats = actob.material_slots + for i, m in enumerate(mats): + if m.name == matname: + # make slot active + actob.active_material_index = i + break + + +def materials_lists_fill_names(context, refresh=False, is_object=False): + mats_list = context.scene.mat_specials_mats + if refresh: + for key in mats_list.keys(): + index = mats_list.find(key) + if index != -1: + mats_list.remove(index) + + obj = context.active_object + mat_collection = [] + if (is_object and obj): + mat_collection = [ + slot.material for slot in obj.material_slots if + slot.material + ] + else: + mat_collection = bpy.data.materials + + for mat in mat_collection: + if mat.name not in mats_list.keys() or refresh: + prop = mats_list.add() + prop.name = mat.name + prop.mat_lib = bool(mat.library) + prop.mat_fake_user = mat.use_fake_user + + +def switch_to_column(is_edit=False): + obj = bpy.context.active_object + collect = obj.material_slots if is_edit else bpy.data.materials + col_size = int(round(len(collect) / COLUMN_SPLIT)) + + return col_size if col_size > 0 else 1 + + +def remove_material_slot(): + if bpy.ops.object.material_slot_remove.poll(): + bpy.ops.object.material_slot_remove() + + def check_mat_name_unique(name_id="Material_new"): # check if the new name pattern is in materials' data name_list = [] @@ -2232,6 +2604,13 @@ def use_remove_mat_all(): return bool(show_rmv_mat) +def use_mat_menu_type(): + pref = return_preferences() + use_menu_mat = pref.set_add_material_menu + + return use_menu_mat + + def use_mat_preview(): pref = return_preferences() show_mat_prw = pref.show_mat_preview @@ -2286,8 +2665,12 @@ def register(): # Register Scene Properties bpy.types.Scene.mat_specials = PointerProperty( - type=material_specials_scene_props - ) + type=material_specials_scene_props + ) + bpy.types.Scene.mat_specials_mats = CollectionProperty( + name="Material name", + type=material_specials_scene_mats + ) kc = bpy.context.window_manager.keyconfigs.addon if kc: @@ -2313,6 +2696,7 @@ def unregister(): bpy.types.MATERIAL_MT_specials.remove(menu_func) del bpy.types.Scene.mat_specials + del bpy.types.Scene.mat_specials_mats bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/materials_utils/material_converter.py b/release/scripts/addons/materials_utils/material_converter.py index dfde5c50f1b9..bf52bec1de3f 100644 --- a/release/scripts/addons/materials_utils/material_converter.py +++ b/release/scripts/addons/materials_utils/material_converter.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- import bpy +import math from mathutils import Vector from bpy.types import Operator from .warning_messages_utils import ( - warning_messages, - c_is_cycles_addon_enabled, - c_data_has_materials, - collect_report, - ) + warning_messages, + c_is_cycles_addon_enabled, + c_data_has_materials, + collect_report, +) # ----------------------------------------------------------------------------- # Globals @@ -665,7 +666,7 @@ def makeCyclesFromBI(cmat): # Make Diffuse and Output nodes mainShader = makeMainShader(TreeNodes) - mainShader.inputs['Roughness'].default_value = cmat.specular_intensity + mainShader.inputs['Roughness'].default_value = math.sqrt(max(cmat.specular_intensity, 0.0)) mainDiffuse = mainShader materialOutput = makeMaterialOutput(TreeNodes) links.new(mainShader.outputs['BSDF'], materialOutput.inputs['Surface']) @@ -752,6 +753,7 @@ def poll(cls, context): def execute(self, context): AutoNode(False, self) + return {'FINISHED'} @@ -766,13 +768,12 @@ class material_convert_selected(Operator): def poll(cls, context): return (bpy.data.filepath != "" and c_data_has_materials() and c_is_cycles_addon_enabled() and - bool(next((obj for obj in context.selected_objects if obj.type == 'MESH'), - None) - ) + bool(next((obj for obj in context.selected_objects if obj.type == 'MESH'), None)) ) def execute(self, context): AutoNode(True, self) + return {'FINISHED'} diff --git a/release/scripts/addons/materials_utils/materials_cycles_converter.py b/release/scripts/addons/materials_utils/materials_cycles_converter.py index c9dd994dbe37..13e1e1ec2ad0 100644 --- a/release/scripts/addons/materials_utils/materials_cycles_converter.py +++ b/release/scripts/addons/materials_utils/materials_cycles_converter.py @@ -6,18 +6,18 @@ from os import path as os_path from bpy.types import Operator from math import ( - log2, ceil, - ) + log2, ceil, sqrt, +) from bpy.props import ( - BoolProperty, - EnumProperty, - ) + BoolProperty, + EnumProperty, +) from .warning_messages_utils import ( - warning_messages, - c_is_cycles_addon_enabled, - c_data_has_materials, - collect_report, - ) + warning_messages, + c_is_cycles_addon_enabled, + c_data_has_materials, + collect_report, +) # ----------------------------------------------------------------------------- # Globals @@ -130,7 +130,7 @@ def BakingText(tex, mode, tex_type=None): img = bpy.data.images.get("TMP_BAKING") img.file_format = ("JPEG" if not mode == "ALPHA" else "PNG") - # switch temporarly to 'IMAGE EDITOR', other approaches are not reliable + # switch temporarily to 'IMAGE EDITOR', other approaches are not reliable check_area = False store_area = bpy.context.area.type collect_report("INFO: Temporarly switching context to Image Editor") @@ -267,8 +267,7 @@ def AutoNode(active=False, operator=None): for n in TreeNodes.nodes: TreeNodes.nodes.remove(n) - # Starting point is diffuse BSDF and output material - # and a Color Ramp node + # Starting point is diffuse BSDF and output material and a Color Ramp node shader = TreeNodes.nodes.new('ShaderNodeBsdfDiffuse') shader.location = 10, 10 shader_val = TreeNodes.nodes.new('ShaderNodeValToRGB') @@ -382,7 +381,7 @@ def AutoNode(active=False, operator=None): else: # Create Clay Material (Diffuse, Glossy, Layer Weight) shader.inputs['Color'].default_value = PAINT_SC_COLOR - shader.inputs['Roughness'].default_value = 0.9 + shader.inputs['Roughness'].default_value = 0.9486 # remove Color Ramp and links from the default shader and reroute try: @@ -416,10 +415,10 @@ def AutoNode(active=False, operator=None): shader.inputs['Roughness'].default_value = cmat.specular_intensity if shader.type == 'ShaderNodeBsdfGlossy': - shader.inputs['Roughness'].default_value = 1 - cmat.raytrace_mirror.gloss_factor + shader.inputs['Roughness'].default_value = sqrt(max(1 - cmat.raytrace_mirror.gloss_factor, 0.0)) if shader.type == 'ShaderNodeBsdfGlass': - shader.inputs['Roughness'].default_value = 1 - cmat.raytrace_mirror.gloss_factor + shader.inputs['Roughness'].default_value = sqrt(max(1 - cmat.raytrace_mirror.gloss_factor, 0.0)) shader.inputs['IOR'].default_value = cmat.raytrace_transparency.ior if shader.type == 'ShaderNodeEmission': @@ -848,6 +847,21 @@ def create_mix_node(TreeNodes, links, nodes, loc, start, median_point, row, fram return mix_node +def unwrap_active_object(context): + enable_unwrap = context.scene.mat_specials.UV_UNWRAP + if enable_unwrap: + obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT") + try: + # it's possible that the active object would fail UV Unwrap + bpy.ops.object.editmode_toggle() + bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) + bpy.ops.object.editmode_toggle() + collect_report("INFO: UV Unwrapping active object {}".format(obj_name)) + except: + collect_report("ERROR: UV Unwrapping failed for " + "active object {}".format(obj_name)) + + # ----------------------------------------------------------------------------- # Operator Classes @@ -866,10 +880,8 @@ def execute(self, context): TreeNodes = cmat.node_tree for n in TreeNodes.nodes: if n.type == 'ShaderNodeOutputMaterial': - if n.label == 'Locked': - n.label = '' - else: - n.label = 'Locked' + n.label = "" if n.label == "Locked" else "Locked" + return {'FINISHED'} @@ -882,28 +894,17 @@ class mlrefresh(Operator): @classmethod def poll(cls, context): - return (bpy.data.filepath != ""and c_is_cycles_addon_enabled() and + return (bpy.data.filepath != "" and c_is_cycles_addon_enabled() and c_data_has_materials()) def execute(self, context): AutoNodeInitiate(False, self) if CHECK_AUTONODE is True: - enable_unwrap = bpy.context.scene.mat_specials.UV_UNWRAP - if enable_unwrap: - obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT") - try: - # it's possible to the active object would fail UV Unwrap - bpy.ops.object.editmode_toggle() - bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) - bpy.ops.object.editmode_toggle() - collect_report("INFO: UV Unwrapping active object " - "{}".format(obj_name)) - except: - collect_report("ERROR: UV Unwrapping failed for " - "active object {}".format(obj_name)) + unwrap_active_object(context) collect_report("Conversion finished !", False, True) + return {'FINISHED'} @@ -922,20 +923,10 @@ def poll(cls, context): def execute(self, context): AutoNodeInitiate(True, self) if CHECK_AUTONODE is True: - obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT") - enable_unwrap = bpy.context.scene.mat_specials.UV_UNWRAP - if enable_unwrap: - try: - # you can already guess it, what could happen here - bpy.ops.object.editmode_toggle() - bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001) - bpy.ops.object.editmode_toggle() - collect_report("INFO: UV Unwrapping object {}".format(obj_name)) - except: - collect_report("ERROR: UV Unwrapping failed for " - "object {}".format(obj_name)) + unwrap_active_object(context) collect_report("Conversion finished !", False, True) + return {'FINISHED'} @@ -947,27 +938,28 @@ class mlrestore(Operator): bl_options = {'REGISTER', 'UNDO'} switcher = BoolProperty( - name="Use Nodes", - description="When restoring, switch Use Nodes On/Off", - default=True - ) + name="Use Nodes", + description="When restoring, switch Use Nodes On/Off", + default=True + ) renderer = EnumProperty( - name="Renderer", - description="Choose Cycles or Blender Internal", - items=(('CYCLES', "Cycles", "Switch to Cycles"), - ('BI', "Blender Internal", "Switch to Blender Internal")), - default='CYCLES', - ) + name="Renderer", + description="Choose Cycles or Blender Internal", + items=( + ('CYCLES', "Cycles", "Switch to Cycles"), + ('BI', "Blender Internal", "Switch to Blender Internal") + ), + default='CYCLES', + ) @classmethod def poll(cls, context): return c_is_cycles_addon_enabled() def execute(self, context): - if self.switcher: - AutoNodeSwitch(self.renderer, "ON", self) - else: - AutoNodeSwitch(self.renderer, "OFF", self) + switch = "ON" if self.switcher else "OFF" + AutoNodeSwitch(self.renderer, switch, self) + return {'FINISHED'} diff --git a/release/scripts/addons/materials_utils/texture_rename.py b/release/scripts/addons/materials_utils/texture_rename.py index ac728b7011ac..585a3a7d90cd 100644 --- a/release/scripts/addons/materials_utils/texture_rename.py +++ b/release/scripts/addons/materials_utils/texture_rename.py @@ -3,31 +3,41 @@ import bpy from bpy.types import ( - Operator, - Panel, - ) -from bpy.props import StringProperty + Operator, + Panel, +) +from bpy.props import ( + BoolProperty, + StringProperty, +) from .warning_messages_utils import ( - warning_messages, - c_data_has_images, - ) + warning_messages, + c_data_has_images, +) class TEXTURE_OT_patern_rename(Operator): bl_idname = "texture.patern_rename" bl_label = "Texture Renamer" bl_description = ("Replace the Texture names pattern with the attached Image ones\n" - "Works on all Textures (Including Brushes) \n \n" - "The First field - the name pattern to replace \n" - "The Second - searches for existing names \n") + "Works on all Textures (Including Brushes)\n" + "The First field - the name pattern to replace\n" + "The Second - search for existing names") bl_options = {'REGISTER', 'UNDO'} def_name = "Texture" # default name is_not_undo = False # prevent drawing props on undo + named = StringProperty( - name="Search for name", - default=def_name - ) + name="Search for name", + description="Enter the name pattern or choose the one from the dropdown list below", + default=def_name + ) + replace_all = BoolProperty( + name="Replace all", + description="Replace all the Textures in the data with the names of the images attached", + default=False + ) @classmethod def poll(cls, context): @@ -35,28 +45,35 @@ def poll(cls, context): def draw(self, context): layout = self.layout - if self.is_not_undo is True: - box = layout.box() - box.prop(self, "named", text="Name pattern", icon="SYNTAX_ON") - layout.separator() + if not self.is_not_undo: + layout.label(text="*Only Undo is available*", icon="INFO") + return + + layout.prop(self, "replace_all") - box = layout.box() - box.prop_search(self, "named", bpy.data, "textures") - else: - layout.label(text="**Only Undo is available**", icon="INFO") + box = layout.box() + box.enabled = not self.replace_all + box.prop(self, "named", text="Name pattern", icon="SYNTAX_ON") + + box = layout.box() + box.enabled = not self.replace_all + box.prop_search(self, "named", bpy.data, "textures") def invoke(self, context, event): self.is_not_undo = True return context.window_manager.invoke_props_dialog(self) + def check(self, context): + return self.is_not_undo + def execute(self, context): errors = [] # collect texture names without images attached - tex_count = 0 # check if there is textures at all + tex_count = len(bpy.data.textures) for texture in bpy.data.textures: try: - if texture and self.named in texture.name and texture.type in {"IMAGE"}: - tex_count += 1 + is_allowed = self.named in texture.name if not self.replace_all else True + if texture and is_allowed and texture.type in {"IMAGE"}: textname = "" img = (bpy.data.textures[texture.name].image if bpy.data.textures[texture.name] else None) if not img: @@ -67,7 +84,7 @@ def execute(self, context): else: break texture.name = textname - if texture.type != "IMAGE": # rename specific textures as clouds, environnement map,... + if texture.type != "IMAGE": # rename specific textures as clouds, environment map... texture.name = texture.type.lower() except: continue @@ -86,9 +103,7 @@ def execute(self, context): class TEXTURE_PT_rename_panel(Panel): - # Creates a Panel in the scene context of the properties editor bl_label = "Texture Rename" - bl_idname = "SCENE_PT_layout" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "texture" @@ -99,13 +114,13 @@ def draw(self, context): def register(): - bpy.utils.register_module(__name__) - pass + bpy.utils.register_class(TEXTURE_OT_patern_rename) + bpy.utils.register_class(TEXTURE_PT_rename_panel) def unregister(): - bpy.utils.unregister_module(__name__) - pass + bpy.utils.unregister_class(TEXTURE_PT_rename_panel) + bpy.utils.unregister_class(TEXTURE_OT_patern_rename) if __name__ == "__main__": diff --git a/release/scripts/addons/materials_utils/warning_messages_utils.py b/release/scripts/addons/materials_utils/warning_messages_utils.py index 2e5f3b695cc4..00f4a719c7bd 100644 --- a/release/scripts/addons/materials_utils/warning_messages_utils.py +++ b/release/scripts/addons/materials_utils/warning_messages_utils.py @@ -21,7 +21,7 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None, # a list of strings can be passed and concatenated in obj_name too # is_mat a switch to change to materials or textures for obj_name('MAT','TEX', 'FILE', None) # fake - optional string that can be passed - # MAX_COUNT - max members of an list to be displayed + # MAX_COUNT - max members of an list to be displayed in UI report # override - important messages that should be enabled, no matter the setting # pass the show_warnings bool to enable/disable them @@ -72,6 +72,8 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None, "not cleaned"), 'C_OB_MIX_NO_MAT': "{}{}".format(obj_name, "No Materials or an Object type that " "can't have Materials (Clean Material Slots)"), + 'C_OB_MIX_SLOT_MAT': "{}{}".format(obj_name, "No Materials or only empty Slots are removed " + "(Clean Material Slots)"), 'R_OB_NO_MAT': "{}{}".format(obj_name, "No Materials. Nothing to remove"), 'R_OB_FAIL_MAT': "{}{}".format(obj_name, "Failed to remove materials - (Operator Error)"), 'R_NO_SL_MAT': "No Selection. Material slots are not removed", @@ -79,6 +81,7 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None, 'R_ALL_NO_MAT': "Object(s) have no materials to remove", 'R_ACT_MAT': "{}{}".format(obj_name, "Removed active Material"), 'R_ACT_MAT_ALL': "{}{}".format(obj_name, "Removed all Material from the Object"), + 'SL_MAT_EDIT_BY_NAME': "{}{}{}".format("Geometry with the Material ", obj_name, "been selected"), 'SL_MAT_BY_NAME': "{}{}{}".format("Objects with the Material ", obj_name, "been selected"), 'OB_CANT_MAT': "{}{}".format(obj_name, "Object type that can't have Materials"), 'REP_MAT_NONE': "Replace Material: No materials replaced", @@ -123,17 +126,18 @@ def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None, operator.report({'INFO'}, message[warn]) if obj_size_big is True: - print("\n** MATERIAL SPECIALS **: \n Full list for the Info message is: \n", - ", ".join(object_name), "\n") + print("\n[Materials Utils Specials]:\nFull list for the Info message is:\n\n", + " ".join(names + "," + "\n" * ((i + 1) % 10 == 0) for i, names in enumerate(object_name)), + "\n") - # restore settings if overriden + # restore settings if overridden if override: addon.preferences.show_warnings = get_warn def collect_report(collection="", is_start=False, is_final=False): # collection passes a string for appending to COLLECT_REPORT global - # is_final swithes to the final report with the operator in __init__ + # is_final switches to the final report with the operator in __init__ global COLLECT_REPORT scene = bpy.context.scene.mat_specials use_report = scene.enable_report @@ -164,20 +168,14 @@ def c_data_has_materials(): return (len(bpy.data.materials) > 0) +def c_obj_data_has_materials(obj): + # check for material presence in object's data + matlen = 0 + if obj: + matlen = len(obj.data.materials) + return (matlen > 0) + + def c_data_has_images(): # check for image presence in data return (len(bpy.data.images) > 0) - - -def register(): - bpy.utils.register_module(__name__) - pass - - -def unregister(): - bpy.utils.unregister_module(__name__) - pass - - -if __name__ == "__main__": - register() diff --git a/release/scripts/addons/measureit/__init__.py b/release/scripts/addons/measureit/__init__.py index a07eb66224e5..489e0a9e6651 100644 --- a/release/scripts/addons/measureit/__init__.py +++ b/release/scripts/addons/measureit/__init__.py @@ -47,10 +47,10 @@ import importlib importlib.reload(measureit_main) - print("measureit: Reloaded multifiles") + # print("measureit: Reloaded multifiles") else: from . import measureit_main - print("measureit: Imported multifiles") + # print("measureit: Imported multifiles") # noinspection PyUnresolvedReferences import bpy diff --git a/release/scripts/addons/measureit/measureit_main.py b/release/scripts/addons/measureit/measureit_main.py index 89a48799a438..444ade73b6ad 100644 --- a/release/scripts/addons/measureit/measureit_main.py +++ b/release/scripts/addons/measureit/measureit_main.py @@ -2038,10 +2038,9 @@ def draw_main(context): if 'MeasureGenerator' in myobj: # verify visible layer for x in range(0, 20): - if myobj.layers[x] is True: - if x in layers: - op = myobj.MeasureGenerator[0] - draw_segments(context, myobj, op, region, rv3d) + if myobj.layers[x] is True and x in layers: + op = myobj.MeasureGenerator[0] + draw_segments(context, myobj, op, region, rv3d) break # --------------------------------------- # Generate all OpenGL calls for debug diff --git a/release/scripts/addons/mesh_carver.py b/release/scripts/addons/mesh_carver.py index aa388a1580f9..16308aa4e7c4 100644 --- a/release/scripts/addons/mesh_carver.py +++ b/release/scripts/addons/mesh_carver.py @@ -21,37 +21,44 @@ "name": "Carver MT", "category": "Object", "author": "Pixivore, Cedric LEPILLER, Ted Milker", - "version": (1, 1, 7), - "blender": (2, 77, 0), + "version": (1, 1, 8), + "blender": (2, 79, 2), "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/Modeling/Carver", "description": "Multiple tools to carve or to create objects", - } +} import bpy import bgl import blf import math -import mathutils import sys import random import bmesh +from mathutils import ( + Color, + Euler, + Matrix, + Vector, + Quaternion, +) import bpy_extras from bpy.props import ( - BoolProperty, - EnumProperty, - IntProperty, - StringProperty, - ) + BoolProperty, + IntProperty, + PointerProperty, + StringProperty, +) from bpy_extras import view3d_utils from bpy_extras.view3d_utils import ( - region_2d_to_vector_3d, - region_2d_to_location_3d, - ) + region_2d_to_vector_3d, + region_2d_to_location_3d, +) + Profils = [ ("CTP_4882", - mathutils.Vector((2.61824, -5.56469, 0)), + Vector((2.61824, -5.56469, 0)), [(-1.156501, 0.799282, 0.032334), (-0.967583, 0.838861, 0.032334), (-1.10386, 0.846403, 0.032334), @@ -108,7 +115,7 @@ [38, 39, 47], [34, 32, 35], [39, 46, 47], [43, 34, 35], [35, 38, 47], [47, 42, 35], [32, 33, 35], [42, 40, 43], [36, 37, 39], [42, 43, 35], [44, 45, 47], [46, 44, 47]]), ("CTP_8354", - mathutils.Vector((-0.06267, -2.43829, -0.0)), + Vector((-0.06267, -2.43829, -0.0)), [(-0.534254, -1.0, 0.032334), (-1.0, -0.534254, 0.032334), (-0.654798, -0.98413, 0.032334), @@ -212,7 +219,7 @@ [48, 71, 80, 47], [67, 37, 38, 83], [82, 83, 87, 86], [81, 82, 86, 85], [80, 81, 85, 84], [47, 80, 84, 46], [83, 38, 39, 87]]), ("CTP_5585", - mathutils.Vector((5.0114, -2.4281, 0.0)), + Vector((5.0114, -2.4281, 0.0)), [(-0.490711, -1.0, 0.032334), (-1.0, -0.490711, 0.032334), (1.0, -0.490711, 0.032334), @@ -233,7 +240,7 @@ [[11, 12, 13, 14], [9, 8, 4, 1], [10, 9, 1, 0], [11, 10, 0, 3], [12, 11, 3, 2], [13, 12, 2, 7], [14, 13, 7, 6], [15, 14, 6, 5], [8, 15, 5, 4], [9, 10, 15, 8], [10, 11, 14, 15]]), ("CTP_6960", - mathutils.Vector((-0.11417, 2.48371, -0.0)), + Vector((-0.11417, 2.48371, -0.0)), [(0.0, 1.0, 0.016827), (-0.382683, 0.92388, 0.016827), (-0.707107, 0.707107, 0.016827), @@ -271,7 +278,7 @@ [6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0], [0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]), ("CTP_5359", - mathutils.Vector((5.50446, 2.41669, -0.0)), + Vector((5.50446, 2.41669, -0.0)), [(0.0, 0.714247, 0.023261), (-0.382683, 0.659879, 0.023261), (-0.707107, 0.505049, 0.023261), @@ -309,7 +316,7 @@ [6, 22, 21, 5], [14, 30, 29, 13], [7, 23, 22, 6], [15, 31, 30, 14], [8, 24, 23, 7], [1, 17, 16, 0], [0, 16, 31, 15], [9, 25, 24, 8], [2, 18, 17, 1], [10, 26, 25, 9]]), ("CTP_5424", - mathutils.Vector((2.61824, 2.34147, 0.0)), + Vector((2.61824, 2.34147, 0.0)), [(1.0, -1.0, 0.032334), (-1.0, 1.0, 0.032334), (1.0, 1.0, 0.032334), @@ -328,7 +335,7 @@ [[3, 0, 2], [10, 9, 2], [2, 1, 4], [2, 4, 13], [5, 3, 2], [6, 5, 2], [2, 13, 12], [2, 12, 11], [7, 6, 2], [8, 7, 2], [2, 11, 10], [9, 8, 2]]), ("CTP_3774", - mathutils.Vector((2.61824, -2.52425, 0.0)), + Vector((2.61824, -2.52425, 0.0)), [(1.0, 0.0, 0.020045), (-1.0, 0.0, 0.020045), (0.31903, -0.664947, 0.020045), @@ -367,7 +374,7 @@ [11, 30, 9, 12], [17, 16, 27, 26], [14, 17, 26, 24], [24, 25, 0, 14], [15, 13, 27, 16], [9, 30, 23, 4], [31, 29, 7, 18], [28, 31, 18, 21], [30, 28, 21, 23]]), ("CTP_4473", - mathutils.Vector((7.31539, 0.0, 0.0)), + Vector((7.31539, 0.0, 0.0)), [(0.24549, -1.0, 0.022454), (-0.24549, -1.0, 0.022454), (-0.24549, 1.0, 0.022454), @@ -383,7 +390,7 @@ ], [[8, 3, 2, 10], [0, 9, 11, 1], [4, 8, 9, 5], [8, 10, 11, 9], [10, 7, 6, 11]]), ("CTP_4003", - mathutils.Vector((4.91276, 0.0, 0.0)), + Vector((4.91276, 0.0, 0.0)), [(-1.0, -1.0, 0.026945), (1.0, -1.0, 0.026945), (-1.0, 1.0, 0.026945), @@ -424,7 +431,7 @@ [23, 25, 31, 29], [24, 23, 29, 30], [25, 22, 28, 31], [26, 22, 15, 20], [10, 27, 33, 5], [31, 28, 3, 11], [33, 30, 13, 14], [29, 31, 11, 12], [5, 33, 14, 1], [30, 29, 12, 13], [32, 28, 22, 26]]), ("CTP_3430", - mathutils.Vector((2.61824, 0.0, 0.0)), + Vector((2.61824, 0.0, 0.0)), [(-1.0, -1.0, 0.032334), (1.0, -1.0, 0.032334), (-1.0, 1.0, 0.032334), @@ -432,7 +439,7 @@ ], [[0, 1, 3, 2]]), ("CTP_7175", - mathutils.Vector((0.0, 0.0, 0.0)), + Vector((0.0, 0.0, 0.0)), [(-1.0, -1.0, 0.032334), (1.0, -1.0, 0.032334), (-1.0, 1.0, 0.032334), @@ -464,33 +471,33 @@ class CarverPrefs(bpy.types.AddonPreferences): bl_idname = __name__ Enable_Tab_01 = BoolProperty( - name="Info", - description="Some general information and settings about the add-on", - default=False - ) + name="Info", + description="Some general information and settings about the add-on", + default=False + ) Enable_Tab_02 = BoolProperty( - name="Hotkeys", - description="List of the shortcuts used during carving", - default=False - ) + name="Hotkeys", + description="List of the shortcuts used during carving", + default=False + ) bpy.types.Scene.Key_Create = StringProperty( - name="Object creation", - description="Object creation", - maxlen=1, - default="C" - ) + name="Object creation", + description="Object creation", + maxlen=1, + default="C" + ) bpy.types.Scene.Key_Update = StringProperty( - name="Auto Bevel Update", - description="Auto Bevel Update", - maxlen=1, - default="A", - ) + name="Auto Bevel Update", + description="Auto Bevel Update", + maxlen=1, + default="A", + ) bpy.types.Scene.Key_Bool = StringProperty( - name="Boolean type", - description="Boolean operation type", - maxlen=1, - default="T", - ) + name="Boolean type", + description="Boolean operation type", + maxlen=1, + default="T", + ) bpy.types.Scene.Key_Brush = StringProperty( name="Brush Mode", description="Brush Mode", @@ -498,167 +505,150 @@ class CarverPrefs(bpy.types.AddonPreferences): default="B", ) bpy.types.Scene.Key_Help = StringProperty( - name="Help display", - description="Help display", - maxlen=1, - default="H", - ) + name="Help display", + description="Help display", + maxlen=1, + default="H", + ) bpy.types.Scene.Key_Instant = StringProperty( - name="Instantiate", - description="Instantiate object", - maxlen=1, - default="I", - ) + name="Instantiate", + description="Instantiate object", + maxlen=1, + default="I", + ) bpy.types.Scene.Key_Close = StringProperty( - name="Close polygonal shape", - description="Close polygonal shape", - maxlen=1, - default="X", - ) + name="Close polygonal shape", + description="Close polygonal shape", + maxlen=1, + default="X", + ) bpy.types.Scene.Key_Apply = StringProperty( - name="Apply operation", - description="Apply operation", - maxlen=1, - default="Q", - ) + name="Apply operation", + description="Apply operation", + maxlen=1, + default="Q", + ) bpy.types.Scene.Key_Scale = StringProperty( - name="Scale object", - description="Scale object", - maxlen=1, - default="S", - ) + name="Scale object", + description="Scale object", + maxlen=1, + default="S", + ) bpy.types.Scene.Key_Gapy = StringProperty( - name="Gap rows", - description="Scale gap between columns", - maxlen=1, - default="J", - ) + name="Gap rows", + description="Scale gap between columns", + maxlen=1, + default="J", + ) bpy.types.Scene.Key_Gapx = StringProperty( - name="Gap columns", - description="Scale gap between columns", - maxlen=1, - default="U", - ) + name="Gap columns", + description="Scale gap between columns", + maxlen=1, + default="U", + ) bpy.types.Scene.Key_Depth = StringProperty( - name="Depth", - description="Cursor depth or solidify pattern", - maxlen=1, - default="D", - ) + name="Depth", + description="Cursor depth or solidify pattern", + maxlen=1, + default="D", + ) bpy.types.Scene.Key_BrushDepth = StringProperty( - name="Brush Depth", - description="Brush depth", - maxlen=1, - default="C", - ) + name="Brush Depth", + description="Brush depth", + maxlen=1, + default="C", + ) bpy.types.Scene.Key_Subadd = StringProperty( - name="Add subdivision", - description="Add subdivision", - maxlen=1, - default="X", - ) + name="Add subdivision", + description="Add subdivision", + maxlen=1, + default="X", + ) bpy.types.Scene.Key_Subrem = StringProperty( - name="Remove subdivision", - description="Remove subdivision", - maxlen=1, - default="W", - ) + name="Remove subdivision", + description="Remove subdivision", + maxlen=1, + default="W", + ) bpy.types.Scene.Key_Randrot = StringProperty( - name="Random rotation", - description="Random rotation", - maxlen=1, - default="R", - ) - bpy.types.Scene.Key_Solver = StringProperty( - name="Solver", - description="Switch between Carve and BMesh Boolean solver\n" - "depending on a specific use case", - maxlen=1, - default="V", - ) + name="Random rotation", + description="Random rotation", + maxlen=1, + default="R", + ) bpy.types.Scene.ProfilePrefix = StringProperty( - name="Profile prefix", - description="Prefix to look for profiles with", - default="Carver_Profile-" - ) - bpy.types.Scene.CarverSolver = EnumProperty( - name="Boolean Solver", - description="Boolean solver to use by default\n", - default="CARVE", - items=( - ('CARVE', 'Carve', "Carve solver, as the legacy one, can handle\n" - "basic coplanar but can often fail with\n" - "non-closed geometry"), - ('BMESH', 'BMesh', "BMesh solver is faster, but cannot handle\n" - "coplanar and self-intersecting geometry") - ) - ) + name="Profile prefix", + description="Prefix to look for profiles with", + default="Carver_Profile-" + ) def draw(self, context): scene = context.scene layout = self.layout - layout.prop(self, "Enable_Tab_01", text="Info and Settings", icon="QUESTION") + icon_1 = "TRIA_RIGHT" if not self.Enable_Tab_01 else "TRIA_DOWN" + box = layout.box() + box.prop(self, "Enable_Tab_01", text="Info and Settings", emboss=False, icon=icon_1) if self.Enable_Tab_01: - layout.label(text="Carver Operator:", icon="LAYER_ACTIVE") - layout.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve", + box.label(text="Carver Operator:", icon="LAYER_ACTIVE") + box.label(text="Select a Mesh Object and press [CTRL]+[SHIFT]+[X] to carve", icon="LAYER_USED") - layout.label(text="To finish carving press [ESC] or [RIGHT CLICK]", + box.label(text="To finish carving press [ESC] or [RIGHT CLICK]", icon="LAYER_USED") + box.prop(scene, "ProfilePrefix", text="Profile prefix") - layout.prop(scene, "ProfilePrefix", text="Profile prefix") - layout.prop(scene, "CarverSolver", text="Solver") - - layout.prop(self, "Enable_Tab_02", text="Keys", icon="KEYINGSET") + icon_2 = "TRIA_RIGHT" if not self.Enable_Tab_02 else "TRIA_DOWN" + box = layout.box() + box.prop(self, "Enable_Tab_02", text="Keys", emboss=False, icon=icon_2) if self.Enable_Tab_02: - split = layout.split() - col = split.column() + split = box.split(align=True) + box = split.box() + col = box.column(align=True) col.label("Object Creation:") col.prop(scene, "Key_Create", text="") col.label("Auto bevel update:") col.prop(scene, "Key_Update", text="") col.label("Boolean operation type:") col.prop(scene, "Key_Bool", text="") - col.label("Solver:") - col.prop(scene, "Key_Solver", text="") + col.label("Brush Depth:") + col.prop(scene, "Key_BrushDepth", text="") - col = split.column() + box = split.box() + col = box.column(align=True) col.label("Brush Mode:") col.prop(scene, "Key_Brush", text="") col.label("Help display:") col.prop(scene, "Key_Help", text="") col.label("Instantiate object:") col.prop(scene, "Key_Instant", text="") - col.label("Brush Depth:") - col.prop(scene, "Key_BrushDepth", text="") + col.label("Random rotation:") + col.prop(scene, "Key_Randrot", text="") - col = split.column() + box = split.box() + col = box.column(align=True) col.label("Close polygonal shape:") col.prop(scene, "Key_Close", text="") col.label("Apply operation:") col.prop(scene, "Key_Apply", text="") col.label("Scale object:") col.prop(scene, "Key_Scale", text="") + col.label("Subdiv add:") + col.prop(scene, "Key_Subadd", text="") - col = split.column() + box = split.box() + col = box.column(align=True) col.label("Gap rows:") col.prop(scene, "Key_Gapy", text="") col.label("Gap columns:") col.prop(scene, "Key_Gapx", text="") col.label("Depth / Solidify:") col.prop(scene, "Key_Depth", text="") - - col = split.column() - col.label("Subdiv add:") - col.prop(scene, "Key_Subadd", text="") col.label("Subdiv Remove:") col.prop(scene, "Key_Subrem", text="") - col.label("Random rotation:") - col.prop(scene, "Key_Randrot", text="") # Draw Text (Center position) -def DrawCenterText(text, xt, yt, Size, Color, self): +def DrawCenterText(text, xt, yt, Size, colors, self): font_id = 0 # Offset Shadow Sshadow_x = 2 @@ -670,16 +660,16 @@ def DrawCenterText(text, xt, yt, Size, Color, self): blf.draw(font_id, text) blf.position(font_id, xt - blf.dimensions(font_id, text)[0] / 2, yt, 0) - if Color is not None: - mColor = mathutils.Color((Color[0], Color[1], Color[2])) - bgl.glColor4f(mColor.r, mColor.g, mColor.b, 1.0) + if colors is not None: + mcolor = Color((colors[0], colors[1], colors[2])) + bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0) else: bgl.glColor4f(1.0, 1.0, 1.0, 1.0) blf.draw(font_id, text) # Draw text (Left position) -def DrawLeftText(text, xt, yt, Size, Color, self): +def DrawLeftText(text, xt, yt, Size, colors, self): font_id = 0 # Offset Shadow Sshadow_x = 2 @@ -690,16 +680,16 @@ def DrawLeftText(text, xt, yt, Size, Color, self): bgl.glColor4f(0.0, 0.0, 0.0, 1.0) blf.draw(font_id, text) blf.position(font_id, xt, yt, 0) - if Color is not None: - mColor = mathutils.Color((Color[0], Color[1], Color[2])) - bgl.glColor4f(mColor.r, mColor.g, mColor.b, 1.0) + if colors is not None: + mcolor = Color((colors[0], colors[1], colors[2])) + bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0) else: bgl.glColor4f(1.0, 1.0, 1.0, 1.0) blf.draw(font_id, text) # Draw text (Right position) -def DrawRightText(text, xt, yt, Size, Color, self): +def DrawRightText(text, xt, yt, Size, colors, self): font_id = 0 # Offset Shadow Sshadow_x = 2 @@ -710,9 +700,9 @@ def DrawRightText(text, xt, yt, Size, Color, self): bgl.glColor4f(0.0, 0.0, 0.0, 1.0) blf.draw(font_id, text) blf.position(font_id, xt - blf.dimensions(font_id, text)[0], yt, 0) - if Color is not None: - mColor = mathutils.Color((Color[0], Color[1], Color[2])) - bgl.glColor4f(mColor.r, mColor.g, mColor.b, 1.0) + if colors is not None: + mcolor = Color((colors[0], colors[1], colors[2])) + bgl.glColor4f(mcolor.r, mcolor.g, mcolor.b, 1.0) else: bgl.glColor4f(1.0, 1.0, 1.0, 1.0) blf.draw(font_id, text) @@ -743,28 +733,18 @@ def draw_callback_px(self, context): BooleanMode = "Create" else: if self.ObjectMode or self.ProfileMode: - if self.BoolOps == DIFFERENCE: - BooleanType = "Difference) [T]" - else: - BooleanType = "Union) [T]" - - if self.ObjectMode: - BooleanMode = "Object Brush (" + BooleanType - else: - BooleanMode = "Profil Brush (" + BooleanType + BooleanType = "Difference) [T]" if self.BoolOps == DIFFERENCE else "Union) [T]" + BooleanMode = \ + "Object Brush (" + BooleanType if self.ObjectMode else "Profil Brush (" + BooleanType else: - if (self.shift is False) and (self.ForceRebool is False): - BooleanMode = "Difference" - else: - BooleanMode = "Rebool" + BooleanMode = \ + "Difference" if (self.shift is False) and (self.ForceRebool is False) else "Rebool" UIColor = (0.992, 0.5518, 0.0, 1.0) # Display boolean mode - if region.width >= 850: - DrawCenterText(BooleanMode, xt, yt, 40, UIColor, self) - else: - DrawCenterText(BooleanMode, xt, yt, 20, UIColor, self) + text_size = 40 if region.width >= 850 else 20 + DrawCenterText(BooleanMode, xt, yt, text_size, UIColor, self) # Separator (Line) LineWidth = 75 @@ -790,12 +770,11 @@ def draw_callback_px(self, context): # Variables according to screen size IFontSize = 12 yInterval = 20 - xCmd = 0 yCmd = yt - 30 + if region.width >= 850: IFontSize = 18 yInterval = 25 - xCmd = 100 # Color Color0 = None @@ -816,11 +795,9 @@ def draw_callback_px(self, context): # Depth Cursor TypeStr = "Cursor Depth [" + context.scene.Key_Depth + "] : " - if self.snapCursor: - BoolStr = "(ON)" - else: - BoolStr = "(OFF)" + BoolStr = "(ON)" if self.snapCursor else "(OFF)" OpsStr = TypeStr + BoolStr + TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0] @@ -830,11 +807,9 @@ def draw_callback_px(self, context): if self.CreateMode is False: # Apply Booleans TypeStr = "Apply Operations [" + context.scene.Key_Apply + "] : " - if self.DontApply: - BoolStr = "(OFF)" - else: - BoolStr = "(ON)" + BoolStr = "(OFF)" if self.DontApply else "(ON)" OpsStr = TypeStr + BoolStr + TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0] @@ -843,11 +818,9 @@ def draw_callback_px(self, context): # Auto update for bevel TypeStr = "Bevel Update [" + context.scene.Key_Update + "] : " - if self.Auto_BevelUpdate: - BoolStr = "(ON)" - else: - BoolStr = "(OFF)" + BoolStr = "(ON)" if self.Auto_BevelUpdate else "(OFF)" OpsStr = TypeStr + BoolStr + TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0] @@ -856,10 +829,7 @@ def draw_callback_px(self, context): # Subdivisions if self.CutMode == CIRCLE: - if self.CreateMode is False: - y = yCmd - yInterval * 4 - else: - y = yCmd - yInterval * 2 + y = yCmd - yInterval * 4 if self.CreateMode is False else yCmd - yInterval * 2 TypeStr = "Subdivisions [" + context.scene.Key_Subrem + "][" + context.scene.Key_Subadd + "] : " BoolStr = str((int(360 / self.stepAngle[self.step]))) OpsStr = TypeStr + BoolStr @@ -872,11 +842,9 @@ def draw_callback_px(self, context): else: # INSTANTIATE: TypeStr = "Instantiate [" + context.scene.Key_Instant + "] : " - if self.Instantiate: - BoolStr = "(ON)" - else: - BoolStr = "(OFF)" + BoolStr = "(ON)" if self.Instantiate else "(OFF)" OpsStr = TypeStr + BoolStr + blf.size(font_id, IFontSize, 72) TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 @@ -887,11 +855,9 @@ def draw_callback_px(self, context): # RANDOM ROTATION: if self.alt: TypeStr = "Random Rotation [" + context.scene.Key_Randrot + "] : " - if self.RandomRotation: - BoolStr = "(ON)" - else: - BoolStr = "(OFF)" + BoolStr = "(ON)" if self.RandomRotation else "(OFF)" OpsStr = TypeStr + BoolStr + blf.size(font_id, IFontSize, 72) TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 @@ -911,42 +877,38 @@ def draw_callback_px(self, context): TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0] - if self.alt: - DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self) - DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self) - else: - DrawLeftText(TypeStr, xLeft, yCmd - yInterval, IFontSize, Color0, self) - DrawLeftText(BoolStr, xLeftP, yCmd - yInterval, IFontSize, Color1, self) + + self_alt_y = 2 if self.alt else 1 + DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self) + DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self) # BRUSH DEPTH: if (self.ObjectMode): TypeStr = "Carve Depth [" + context.scene.Key_Depth + "] : " BoolStr = str(round(self.ObjectBrush.data.vertices[0].co.z, 2)) OpsStr = TypeStr + BoolStr + blf.size(font_id, IFontSize, 72) TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0] - if self.alt: - DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self) - DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self) - else: - DrawLeftText(TypeStr, xLeft, yCmd - yInterval, IFontSize, Color0, self) - DrawLeftText(BoolStr, xLeftP, yCmd - yInterval, IFontSize, Color1, self) + + self_alt_y = 2 if self.alt else 1 + DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self) + DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self) TypeStr = "Brush Depth [" + context.scene.Key_BrushDepth + "] : " BoolStr = str(round(self.BrushDepthOffset, 2)) OpsStr = TypeStr + BoolStr + blf.size(font_id, IFontSize, 72) TotalWidth = blf.dimensions(font_id, OpsStr)[0] xLeft = region.width / 2 - TotalWidth / 2 xLeftP = xLeft + blf.dimensions(font_id, TypeStr)[0] - if self.alt: - DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 3, IFontSize, Color0, self) - DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 3, IFontSize, Color1, self) - else: - DrawLeftText(TypeStr, xLeft, yCmd - yInterval * 2, IFontSize, Color0, self) - DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * 2, IFontSize, Color1, self) + + self_alt_y = 3 if self.alt else 2 + DrawLeftText(TypeStr, xLeft, yCmd - yInterval * self_alt_y, IFontSize, Color0, self) + DrawLeftText(BoolStr, xLeftP, yCmd - yInterval * self_alt_y, IFontSize, Color1, self) bgl.glEnable(bgl.GL_BLEND) if region.width >= 850: @@ -969,21 +931,23 @@ def draw_callback_px(self, context): if self.ObjectMode or self.ProfileMode: if self.ProfileMode: DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp + - Help_Interval * 2, Help_FontSize, UIColor, self) + Help_Interval * 2, Help_FontSize, UIColor, self) DrawLeftText(": Object Mode", 150 + t_panel_width, yHelp + - Help_Interval * 2, Help_FontSize, None, self) + Help_Interval * 2, Help_FontSize, None, self) else: DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp + - Help_Interval * 2, Help_FontSize, UIColor, self) - DrawLeftText(": Return", 150 + t_panel_width, yHelp + Help_Interval * 2, Help_FontSize, None, self) + Help_Interval * 2, Help_FontSize, UIColor, self) + DrawLeftText(": Return", 150 + t_panel_width, yHelp + + Help_Interval * 2, Help_FontSize, None, self) else: DrawLeftText("[" + context.scene.Key_Brush + "]", xHelp, yHelp + - Help_Interval * 2, Help_FontSize, UIColor, self) + Help_Interval * 2, Help_FontSize, UIColor, self) DrawLeftText(": Profil Brush", 150 + t_panel_width, yHelp + - Help_Interval * 2, Help_FontSize, None, self) - DrawLeftText("[Ctrl + LMB]", xHelp, yHelp - Help_Interval * 6, Help_FontSize, UIColor, self) + Help_Interval * 2, Help_FontSize, None, self) + DrawLeftText("[Ctrl + LMB]", xHelp, yHelp - Help_Interval * 6, + Help_FontSize, UIColor, self) DrawLeftText(": Move Cursor", 150 + t_panel_width, yHelp - - Help_Interval * 6, Help_FontSize, None, self) + Help_Interval * 6, Help_FontSize, None, self) if (self.ObjectMode is False) and (self.ProfileMode is False): if self.CreateMode is False: @@ -994,235 +958,225 @@ def draw_callback_px(self, context): else: DrawLeftText("[" + context.scene.Key_Create + "]", xHelp, yHelp + Help_Interval, Help_FontSize, UIColor, self) - DrawLeftText(": Cut", 150 + t_panel_width, yHelp + Help_Interval, Help_FontSize, None, self) + DrawLeftText(": Cut", 150 + t_panel_width, yHelp + Help_Interval, + Help_FontSize, None, self) if self.CutMode == RECTANGLE: DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self) DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self) - DrawLeftText("[" + context.scene.Key_Solver + "]", - xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self) DrawLeftText(": Dimension", 150 + t_panel_width, yHelp, Help_FontSize, None, self) - DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self) - DrawLeftText(": Solver [" + context.scene.CarverSolver + "]", 150 + t_panel_width, - yHelp - Help_Interval * 2, Help_FontSize, None, self) + DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, + Help_FontSize, None, self) if self.CutMode == CIRCLE: DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self) DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self) DrawLeftText("[" + context.scene.Key_Subrem + "] [" + context.scene.Key_Subadd + "]", - xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self) + xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self) DrawLeftText("[Ctrl]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self) - DrawLeftText("[" + context.scene.Key_Solver + "]", - xHelp, yHelp - Help_Interval * 4, Help_FontSize, UIColor, self) DrawLeftText(": Rotation and Radius", 150 + t_panel_width, yHelp, Help_FontSize, None, self) - DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self) + DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, + Help_FontSize, None, self) DrawLeftText(": Subdivision", 150 + t_panel_width, yHelp - - Help_Interval * 2, Help_FontSize, None, self) + Help_Interval * 2, Help_FontSize, None, self) DrawLeftText(": Incremental rotation", 150 + t_panel_width, - yHelp - Help_Interval * 3, Help_FontSize, None, self) - DrawLeftText(": Solver [" + context.scene.CarverSolver + "]", - 150 + t_panel_width, yHelp - Help_Interval * 4, Help_FontSize, None, self) + yHelp - Help_Interval * 3, Help_FontSize, None, self) if self.CutMode == LINE: DrawLeftText("MouseMove", xHelp, yHelp, Help_FontSize, UIColor, self) DrawLeftText("[Alt]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self) DrawLeftText("[Space]", xHelp, yHelp - Help_Interval * 2, Help_FontSize, UIColor, self) DrawLeftText("[Ctrl]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self) - DrawLeftText("[" + context.scene.Key_Solver + "]", - xHelp, yHelp - Help_Interval * 4, Help_FontSize, UIColor, self) DrawLeftText(": Dimension", 150 + t_panel_width, yHelp, Help_FontSize, None, self) - DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self) + DrawLeftText(": Move all", 150 + t_panel_width, yHelp - Help_Interval, + Help_FontSize, None, self) DrawLeftText(": Validate", 150 + t_panel_width, yHelp - Help_Interval * 2, Help_FontSize, None, self) DrawLeftText(": Incremental", 150 + t_panel_width, yHelp - - Help_Interval * 3, Help_FontSize, None, self) - DrawLeftText(": Solver [" + context.scene.CarverSolver + "]", - 150 + t_panel_width, yHelp - Help_Interval * 4, Help_FontSize, None, self) + Help_Interval * 3, Help_FontSize, None, self) if self.CreateMode: DrawLeftText("[" + context.scene.Key_Subadd + "]", xHelp, yHelp - - Help_Interval * 4, Help_FontSize, UIColor, self) + Help_Interval * 4, Help_FontSize, UIColor, self) DrawLeftText(": Close geometry", 150 + t_panel_width, yHelp - - Help_Interval * 4, Help_FontSize, None, self) + Help_Interval * 4, Help_FontSize, None, self) else: DrawLeftText("[Space]", xHelp, yHelp + Help_Interval, Help_FontSize, UIColor, self) - DrawLeftText(": Difference", 150 + t_panel_width, yHelp + Help_Interval, Help_FontSize, None, self) + DrawLeftText(": Difference", 150 + t_panel_width, yHelp + Help_Interval, + Help_FontSize, None, self) DrawLeftText("[Shift][Space]", xHelp, yHelp, Help_FontSize, UIColor, self) DrawLeftText(": Rebool", 150 + t_panel_width, yHelp, Help_FontSize, None, self) DrawLeftText("[Alt][Space]", xHelp, yHelp - Help_Interval, Help_FontSize, UIColor, self) - DrawLeftText(": Duplicate", 150 + t_panel_width, yHelp - Help_Interval, Help_FontSize, None, self) + DrawLeftText(": Duplicate", 150 + t_panel_width, yHelp - Help_Interval, + Help_FontSize, None, self) DrawLeftText("[" + context.scene.Key_Scale + "]", xHelp, yHelp - - Help_Interval * 2, Help_FontSize, UIColor, self) - DrawLeftText(": Scale", 150 + t_panel_width, yHelp - Help_Interval * 2, Help_FontSize, None, self) + Help_Interval * 2, Help_FontSize, UIColor, self) + DrawLeftText(": Scale", 150 + t_panel_width, yHelp - Help_Interval * 2, + Help_FontSize, None, self) DrawLeftText("[LMB][Move]", xHelp, yHelp - Help_Interval * 3, Help_FontSize, UIColor, self) - DrawLeftText(": Rotation", 150 + t_panel_width, yHelp - Help_Interval * 3, Help_FontSize, None, self) - DrawLeftText("[Ctrl][LMB][Move]", xHelp, yHelp - Help_Interval * 4, Help_FontSize, UIColor, self) - DrawLeftText(": Step Angle", 150 + t_panel_width, yHelp - Help_Interval * 4, Help_FontSize, None, self) + DrawLeftText(": Rotation", 150 + t_panel_width, yHelp - Help_Interval * 3, + Help_FontSize, None, self) + DrawLeftText("[Ctrl][LMB][Move]", xHelp, yHelp - Help_Interval * 4, + Help_FontSize, UIColor, self) + DrawLeftText(": Step Angle", 150 + t_panel_width, yHelp - Help_Interval * 4, + Help_FontSize, None, self) if self.ProfileMode: DrawLeftText("[" + context.scene.Key_Subadd + "][" + context.scene.Key_Subrem + "]", - xHelp, yHelp - Help_Interval * 5, Help_FontSize, UIColor, self) + xHelp, yHelp - Help_Interval * 5, Help_FontSize, UIColor, self) DrawLeftText(": Previous or Next Profile", 150 + t_panel_width, yHelp - Help_Interval * 5, Help_FontSize, None, self) DrawLeftText("[ARROWS]", xHelp, yHelp - Help_Interval * 6, Help_FontSize, UIColor, self) DrawLeftText(": Create / Delete rows or columns", 150 + t_panel_width, - yHelp - Help_Interval * 6, Help_FontSize, None, self) + yHelp - Help_Interval * 6, Help_FontSize, None, self) DrawLeftText("[" + context.scene.Key_Gapy + "][" + context.scene.Key_Gapx + "]", xHelp, yHelp - Help_Interval * 7, Help_FontSize, UIColor, self) DrawLeftText(": Gap between rows or columns", 150 + t_panel_width, - yHelp - Help_Interval * 7, Help_FontSize, None, self) - DrawLeftText("[" + context.scene.Key_Solver + "]", - xHelp, yHelp - Help_Interval * 8, Help_FontSize, UIColor, self) - DrawLeftText(": Solver [" + context.scene.CarverSolver + "]", - 150 + t_panel_width, yHelp - Help_Interval * 8, Help_FontSize, None, self) + yHelp - Help_Interval * 7, Help_FontSize, None, self) - # Opengl Initialise + # Opengl Initialize bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(0.512, 0.919, 0.04, 1.0) bgl.glLineWidth(2) - # if context.space_data.region_3d.is_perspective is False: - if 1: - bgl.glEnable(bgl.GL_POINT_SMOOTH) - - bgl.glPointSize(6) + bgl.glEnable(bgl.GL_POINT_SMOOTH) + bgl.glPointSize(6) - if self.ProfileMode: - xrect = region.width - t_panel_width - 80 - yrect = 80 - bgl.glColor4f(0.0, 0.0, 0.0, 0.3) - bgl.glRecti(xrect, yrect, xrect + 60, yrect - 60) - - faces = self.Profils[self.nProfil][3] - vertices = self.Profils[self.nProfil][2] - WidthProfil = 50 - location = mathutils.Vector((region.width - t_panel_width - WidthProfil, 50, 0)) - ProfilScale = 20.0 - bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.7) - for f in faces: - if len(f) == 4: - bgl.glBegin(bgl.GL_QUADS) - bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] * - ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z) - bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] * - ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z) - bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] * - ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z) - bgl.glVertex3f(vertices[f[3]][0] * ProfilScale + location.x, vertices[f[3]][1] * - ProfilScale + location.y, vertices[f[3]][2] * ProfilScale + location.z) - bgl.glEnd() - if len(f) == 3: - bgl.glBegin(bgl.GL_TRIANGLES) - bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] * - ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z) - bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] * - ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z) - bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] * - ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z) - bgl.glEnd() - - if self.bDone: - if len(self.mouse_path) > 1: - x0 = self.mouse_path[0][0] - y0 = self.mouse_path[0][1] - x1 = self.mouse_path[1][0] - y1 = self.mouse_path[1][1] - - # Cut Line - if self.CutMode == LINE: - if (self.shift) or (self.CreateMode and self.Closed): - bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5) - - bgl.glBegin(bgl.GL_POLYGON) - for x, y in self.mouse_path: - bgl.glVertex2i(x + self.xpos, y + self.ypos) - bgl.glEnd() - - bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0) - bgl.glBegin(bgl.GL_LINE_STRIP) - for x, y in self.mouse_path: - bgl.glVertex2i(x + self.xpos, y + self.ypos) + if self.ProfileMode: + xrect = region.width - t_panel_width - 80 + yrect = 80 + bgl.glColor4f(0.0, 0.0, 0.0, 0.3) + bgl.glRecti(xrect, yrect, xrect + 60, yrect - 60) + + faces = self.Profils[self.nProfil][3] + vertices = self.Profils[self.nProfil][2] + WidthProfil = 50 + location = Vector((region.width - t_panel_width - WidthProfil, 50, 0)) + ProfilScale = 20.0 + bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.7) + for f in faces: + if len(f) == 4: + bgl.glBegin(bgl.GL_QUADS) + bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] * + ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z) + bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] * + ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z) + bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] * + ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z) + bgl.glVertex3f(vertices[f[3]][0] * ProfilScale + location.x, vertices[f[3]][1] * + ProfilScale + location.y, vertices[f[3]][2] * ProfilScale + location.z) bgl.glEnd() - if (self.CreateMode is False) or (self.CreateMode and self.Closed): - bgl.glBegin(bgl.GL_LINE_STRIP) - bgl.glVertex2i(self.mouse_path[len(self.mouse_path) - 1][0] + self.xpos, - self.mouse_path[len(self.mouse_path) - 1][1] + self.ypos) - bgl.glVertex2i(self.mouse_path[0][0] + self.xpos, self.mouse_path[0][1] + self.ypos) - bgl.glEnd() - - bgl.glPointSize(6) - bgl.glBegin(bgl.GL_POINTS) - for x, y in self.mouse_path: - bgl.glVertex2i(x + self.xpos, y + self.ypos) + if len(f) == 3: + bgl.glBegin(bgl.GL_TRIANGLES) + bgl.glVertex3f(vertices[f[0]][0] * ProfilScale + location.x, vertices[f[0]][1] * + ProfilScale + location.y, vertices[f[0]][2] * ProfilScale + location.z) + bgl.glVertex3f(vertices[f[1]][0] * ProfilScale + location.x, vertices[f[1]][1] * + ProfilScale + location.y, vertices[f[1]][2] * ProfilScale + location.z) + bgl.glVertex3f(vertices[f[2]][0] * ProfilScale + location.x, vertices[f[2]][1] * + ProfilScale + location.y, vertices[f[2]][2] * ProfilScale + location.z) bgl.glEnd() - # Cut rectangle - if self.CutMode == RECTANGLE: + if self.bDone: + if len(self.mouse_path) > 1: + x0 = self.mouse_path[0][0] + y0 = self.mouse_path[0][1] + x1 = self.mouse_path[1][0] + y1 = self.mouse_path[1][1] + + # Cut Line + if self.CutMode == LINE: + if (self.shift) or (self.CreateMode and self.Closed): bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5) - # if SHIFT, fill primitive - if self.shift or self.CreateMode: - bgl.glBegin(bgl.GL_QUADS) - bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) - bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos) - bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos) - bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos) - bgl.glEnd() + bgl.glBegin(bgl.GL_POLYGON) + for x, y in self.mouse_path: + bgl.glVertex2i(x + self.xpos, y + self.ypos) + bgl.glEnd() + bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0) + bgl.glBegin(bgl.GL_LINE_STRIP) + for x, y in self.mouse_path: + bgl.glVertex2i(x + self.xpos, y + self.ypos) + bgl.glEnd() + if (self.CreateMode is False) or (self.CreateMode and self.Closed): bgl.glBegin(bgl.GL_LINE_STRIP) - bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) - bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos) - bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos) - bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos) - bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) + bgl.glVertex2i(self.mouse_path[len(self.mouse_path) - 1][0] + self.xpos, + self.mouse_path[len(self.mouse_path) - 1][1] + self.ypos) + bgl.glVertex2i(self.mouse_path[0][0] + self.xpos, self.mouse_path[0][1] + self.ypos) bgl.glEnd() - bgl.glPointSize(6) - bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0) - bgl.glBegin(bgl.GL_POINTS) + bgl.glPointSize(6) + bgl.glBegin(bgl.GL_POINTS) + for x, y in self.mouse_path: + bgl.glVertex2i(x + self.xpos, y + self.ypos) + bgl.glEnd() + + # Cut rectangle + if self.CutMode == RECTANGLE: + bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5) + + # if SHIFT, fill primitive + if self.shift or self.CreateMode: + bgl.glBegin(bgl.GL_QUADS) bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos) bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos) bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos) bgl.glEnd() - # Circle Cut - if self.CutMode == CIRCLE: - DEG2RAD = 3.14159 / 180 - v0 = mathutils.Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0)) - v1 = mathutils.Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0)) - v0 -= v1 - radius = self.mouse_path[1][0] - self.mouse_path[0][0] - DEG2RAD = 3.14159 / (180.0 / self.stepAngle[self.step]) - if self.ctrl: - self.stepR = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 25 - shift = (3.14159 / (360.0 / 60.0)) * int(self.stepR) - else: - shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50 - - if self.shift or self.CreateMode: - bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5) - bgl.glBegin(bgl.GL_TRIANGLE_FAN) - bgl.glVertex2f(x0 + self.xpos, y0 + self.ypos) - for i in range(0, int(360 / self.stepAngle[self.step])): - degInRad = i * DEG2RAD - bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius, - y0 + self.ypos + math.sin(degInRad + shift) * radius) - bgl.glVertex2f(x0 + self.xpos + math.cos(0 + shift) * radius, - y0 + self.ypos + math.sin(0 + shift) * radius) - bgl.glEnd() - - bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0) - bgl.glBegin(bgl.GL_LINE_LOOP) + bgl.glBegin(bgl.GL_LINE_STRIP) + bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) + bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos) + bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos) + bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos) + bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) + bgl.glEnd() + bgl.glPointSize(6) + + bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0) + bgl.glBegin(bgl.GL_POINTS) + bgl.glVertex2i(x0 + self.xpos, y0 + self.ypos) + bgl.glVertex2i(x1 + self.xpos, y0 + self.ypos) + bgl.glVertex2i(x1 + self.xpos, y1 + self.ypos) + bgl.glVertex2i(x0 + self.xpos, y1 + self.ypos) + bgl.glEnd() + + # Circle Cut + if self.CutMode == CIRCLE: + DEG2RAD = 3.14159 / 180 + v0 = Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0)) + v1 = Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0)) + v0 -= v1 + radius = self.mouse_path[1][0] - self.mouse_path[0][0] + DEG2RAD = 3.14159 / (180.0 / self.stepAngle[self.step]) + if self.ctrl: + self.stepR = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 25 + shift = (3.14159 / (360.0 / 60.0)) * int(self.stepR) + else: + shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50 + + if self.shift or self.CreateMode: + bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 0.5) + bgl.glBegin(bgl.GL_TRIANGLE_FAN) + bgl.glVertex2f(x0 + self.xpos, y0 + self.ypos) for i in range(0, int(360 / self.stepAngle[self.step])): degInRad = i * DEG2RAD bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius, y0 + self.ypos + math.sin(degInRad + shift) * radius) + bgl.glVertex2f(x0 + self.xpos + math.cos(0 + shift) * radius, + y0 + self.ypos + math.sin(0 + shift) * radius) bgl.glEnd() + bgl.glColor4f(UIColor[0], UIColor[1], UIColor[2], 1.0) + bgl.glBegin(bgl.GL_LINE_LOOP) + for i in range(0, int(360 / self.stepAngle[self.step])): + degInRad = i * DEG2RAD + bgl.glVertex2f(x0 + self.xpos + math.cos(degInRad + shift) * radius, + y0 + self.ypos + math.sin(degInRad + shift) * radius) + bgl.glEnd() + if self.ObjectMode or self.ProfileMode: if self.ShowCursor: region = context.region rv3d = context.space_data.region_3d - view_width = context.region.width if self.ObjectMode: ob = self.ObjectBrush @@ -1233,7 +1187,7 @@ def draw_callback_px(self, context): # 50% alpha, 2 pixel width line bgl.glEnable(bgl.GL_BLEND) - bbox = [mat * mathutils.Vector(b) for b in ob.bound_box] + bbox = [mat * Vector(b) for b in ob.bound_box] if self.shift: bgl.glLineWidth(4) @@ -1260,12 +1214,12 @@ def draw_callback_px(self, context): # Object display if self.qRot is not None: ob.location = self.CurLoc - v = mathutils.Vector() + v = Vector() v.x = v.y = 0.0 v.z = self.BrushDepthOffset ob.location += self.qRot * v - e = mathutils.Euler() + e = Euler() e.x = 0.0 e.y = 0.0 e.z = self.aRotZ / 25.0 @@ -1319,10 +1273,10 @@ def isect_line_plane_v3(p0, p1, p_co, p_no, epsilon=1e-6): # The segment is parallel to plane return None + # ---------------------- # generic math functions - def add_v3v3(v0, v1): return ( v0[0] + v1[0], @@ -1358,21 +1312,19 @@ def mul_v3_fl(v0, f): v0[2] * f, ) -# Cut Square - +# Cut Square def CreateCutSquare(self, context): FAR_LIMIT = 10000.0 # New mesh me = bpy.data.meshes.new('CMT_Square') - # New object ob = bpy.data.objects.new('CMT_Square', me) # Save new object self.CurrentObj = ob - # Scene informations - scene = context.scene + + # Scene information region = context.region rv3d = context.region_data coord = self.mouse_path[0][0], self.mouse_path[0][1] @@ -1382,14 +1334,10 @@ def CreateCutSquare(self, context): if self.snapCursor: PlanePoint = context.scene.cursor_location else: - if self.OpsObj is not None: - PlanePoint = self.OpsObj.location - else: - PlanePoint = mathutils.Vector((0.0, 0.0, 0.0)) + PlanePoint = self.OpsObj.location if self.OpsObj is not None else Vector((0.0, 0.0, 0.0)) PlaneNormal = depthLocation PlaneNormalised = PlaneNormal.normalized() - d = -PlanePoint.x * PlaneNormalised.x - PlanePoint.y * PlaneNormalised.y - PlanePoint.z * PlaneNormalised.z # Link object to scene context.scene.objects.link(ob) @@ -1448,20 +1396,15 @@ def CreateCutLine(self, context): ob = bpy.data.objects.new('CMT_Line', me) self.CurrentObj = ob - scene = context.scene region = context.region rv3d = context.region_data coord = self.mouse_path[0][0], self.mouse_path[0][1] depthLocation = region_2d_to_vector_3d(region, rv3d, coord) self.ViewVector = depthLocation - if self.snapCursor: - PlanePoint = context.scene.cursor_location - else: - PlanePoint = mathutils.Vector((0.0, 0.0, 0.0)) + PlanePoint = context.scene.cursor_location if self.snapCursor else Vector((0.0, 0.0, 0.0)) PlaneNormal = depthLocation PlaneNormalised = PlaneNormal.normalized() - d = -PlanePoint.x * PlaneNormalised.x - PlanePoint.y * PlaneNormalised.y - PlanePoint.z * PlaneNormalised.z context.scene.objects.link(ob) @@ -1473,8 +1416,9 @@ def CreateCutLine(self, context): bLine = False - if (len(self.mouse_path) == 2) or ((len(self.mouse_path) <= 3) and (self.mouse_path[1] == self.mouse_path[2])): - PlanePoint = mathutils.Vector((0.0, 0.0, 0.0)) + if (len(self.mouse_path) == 2) or ((len(self.mouse_path) <= 3) and + (self.mouse_path[1] == self.mouse_path[2])): + PlanePoint = Vector((0.0, 0.0, 0.0)) PlaneNormal = depthLocation PlaneNormalised = PlaneNormal.normalized() # Force rebool @@ -1496,7 +1440,6 @@ def CreateCutLine(self, context): Index += 1 if NbVertices == 1: t_v0 = t_bm.verts.new(loc0) - t_init = t_v0 LocInit = loc0 t_bm.verts.index_update() else: @@ -1504,7 +1447,6 @@ def CreateCutLine(self, context): t_edges = t_bm.edges.new([t_v0, t_v1]) NbVertices = 1 t_v0 = t_v1 - else: for x, y in self.mouse_path: v0 = x + self.xpos, y + self.ypos @@ -1518,7 +1460,6 @@ def CreateCutLine(self, context): NbVertices += 1 if NbVertices == 1: t_v0 = t_bm.verts.new(loc0) - t_init = t_v0 LocInit = loc0 t_bm.verts.index_update() FacesList.append(t_v0) @@ -1554,20 +1495,15 @@ def CreateCutCircle(self, context): ob = bpy.data.objects.new('CMT_Circle', me) self.CurrentObj = ob - scene = context.scene region = context.region rv3d = context.region_data coord = self.mouse_path[0][0], self.mouse_path[0][1] depthLocation = region_2d_to_vector_3d(region, rv3d, coord) self.ViewVector = depthLocation - if self.snapCursor: - PlanePoint = context.scene.cursor_location - else: - PlanePoint = mathutils.Vector((0.0, 0.0, 0.0)) + PlanePoint = context.scene.cursor_location if self.snapCursor else Vector((0.0, 0.0, 0.0)) PlaneNormal = depthLocation PlaneNormalised = PlaneNormal.normalized() - d = -PlanePoint.x * PlaneNormalised.x - PlanePoint.y * PlaneNormalised.y - PlanePoint.z * PlaneNormalised.z context.scene.objects.link(ob) @@ -1576,11 +1512,9 @@ def CreateCutCircle(self, context): x0 = self.mouse_path[0][0] y0 = self.mouse_path[0][1] - x1 = self.mouse_path[1][0] - y1 = self.mouse_path[1][1] - v0 = mathutils.Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0)) - v1 = mathutils.Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0)) + v0 = Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0)) + v1 = Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0)) v0 -= v1 radius = self.mouse_path[1][0] - self.mouse_path[0][0] DEG2RAD = math.pi / (180.0 / self.stepAngle[self.step]) @@ -1594,7 +1528,8 @@ def CreateCutCircle(self, context): FacesList = [] for i in range(0, int(360.0 / self.stepAngle[self.step])): degInRad = i * DEG2RAD - v0 = x0 + self.xpos + math.cos(degInRad + shift) * radius, y0 + self.ypos + math.sin(degInRad + shift) * radius + v0 = x0 + self.xpos + math.cos(degInRad + shift) * radius, \ + y0 + self.ypos + math.sin(degInRad + shift) * radius vec = region_2d_to_vector_3d(region, rv3d, v0) loc0 = region_2d_to_location_3d(region, rv3d, v0, vec) @@ -1607,7 +1542,6 @@ def CreateCutCircle(self, context): FacesList.append(t_v0) t_bm.verts.index_update() - t_face = t_bm.faces.new(FacesList) t_bm.to_mesh(me) @@ -1699,9 +1633,8 @@ def update_bevel(context): obj.select = True context.scene.objects.active = active -# Create bevel - +# Create bevel def CreateBevel(context, CurrentObject): # Save active object SavActive = context.active_object @@ -1752,7 +1685,7 @@ def CreateBevel(context, CurrentObject): context.object.data.use_auto_smooth = True context.object.data.auto_smooth_angle = 1.0471975 - # Remet l'objet actif par défaut + # Restore the active object context.scene.objects.active = SavActive @@ -1767,7 +1700,6 @@ def Picking(context, event): # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) - ray_target = ray_origin + view_vector def visible_objects_and_duplis(): @@ -1794,8 +1726,7 @@ def obj_ray_cast(obj, matrix): success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj) if success: return location, normal, face_index - else: - return None, None, None + return None, None, None # cast rays and find the closest object best_length_squared = -1.0 @@ -1820,7 +1751,6 @@ def obj_ray_cast(obj, matrix): def CreatePrimitive(self, _AngleStep, _radius): - CLRaw = [] Angle = 0.0 self.NbPointsInPrimitive = 0 while(Angle < 360.0): @@ -1838,7 +1768,7 @@ def CreatePrimitive(self, _AngleStep, _radius): def MoveCursor(qRot, location, self): if qRot is not None: self.CLR_C.clear() - vc = mathutils.Vector() + vc = Vector() idx = 0 for i in range(int(len(self.CircleListRaw) / 3)): vc.x = self.CircleListRaw[idx * 3] * self.CRadius @@ -1855,12 +1785,12 @@ def RBenVe(Object, Dir): ObjectV = Object.normalized() DirV = Dir.normalized() cosTheta = ObjectV.dot(DirV) - rotationAxis = mathutils.Vector((0.0, 0.0, 0.0)) + rotationAxis = Vector((0.0, 0.0, 0.0)) if (cosTheta < -1 + 0.001): - v = mathutils.Vector((0.0, 1.0, 0.0)) + v = Vector((0.0, 1.0, 0.0)) rotationAxis = ObjectV.cross(v) rotationAxis = rotationAxis.normalized() - q = mathutils.Quaternion() + q = Quaternion() q.w = 0.0 q.x = rotationAxis.x q.y = rotationAxis.y @@ -1869,7 +1799,7 @@ def RBenVe(Object, Dir): rotationAxis = ObjectV.cross(DirV) s = math.sqrt((1.0 + cosTheta) * 2.0) invs = 1 / s - q = mathutils.Quaternion() + q = Quaternion() q.w = s * 0.5 q.x = rotationAxis.x * invs q.y = rotationAxis.y * invs @@ -1878,7 +1808,6 @@ def RBenVe(Object, Dir): def Pick(context, event, self, ray_max=10000.0): - scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y @@ -1893,8 +1822,7 @@ def obj_ray_cast(obj, matrix): success, hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj) if success: return hit, normal, face_index - else: - return None, None, None + return None, None, None best_length_squared = ray_max * ray_max best_obj = None @@ -1914,8 +1842,8 @@ def obj_ray_cast(obj, matrix): if best_obj is not None: return hits, ns, fs, rotation - else: - return None, None, None + + return None, None, None def SelectObject(self, copyobj): @@ -1939,7 +1867,7 @@ def UndoAdd(self, type, OpsObj): return if type != "DUPLICATE": ob = OpsObj - # Creation du mesh de 'sauvegarde' + # Create the 'backup' mesh bm = bmesh.new() bm.from_mesh(ob.data) @@ -2011,7 +1939,7 @@ def duplicateObject(self): ob_new = bpy.context.active_object ob_new.location = self.CurLoc - v = mathutils.Vector() + v = Vector() v.x = v.y = 0.0 v.z = self.BrushDepthOffset ob_new.location += self.qRot * v @@ -2021,7 +1949,7 @@ def duplicateObject(self): if self.ProfileMode: ob_new.scale = self.ProfileBrush.scale - e = mathutils.Euler() + e = Euler() e.x = e.y = 0.0 e.z = self.aRotZ / 25.0 @@ -2075,7 +2003,10 @@ def update_grid(self, context): # Get the data from the profils or the object if self.ProfileMode: - brush = bpy.data.objects.new(self.Profils[self.nProfil][0], bpy.data.meshes[self.Profils[self.nProfil][0]]) + brush = bpy.data.objects.new( + self.Profils[self.nProfil][0], + bpy.data.meshes[self.Profils[self.nProfil][0]] + ) obj = bpy.data.objects["CT_Profil"] obfaces = brush.data.polygons obverts = brush.data.vertices @@ -2107,7 +2038,7 @@ def update_grid(self, context): # Add random rotation if (self.RandomRotation) and not (self.GridScaleX or self.GridScaleY): - rotmat = mathutils.Matrix.Rotation(math.radians(360 * random.random()), 4, 'Z') + rotmat = Matrix.Rotation(math.radians(360 * random.random()), 4, 'Z') for v in obverts: v.co = v.co * rotmat @@ -2124,47 +2055,25 @@ def update_grid(self, context): mymesh.update(calc_edges=True) # Update data obj.data = mymesh - # Make the the object the active one to remove double + # Make the object active to remove doubles context.scene.objects.active = obj -def boolean_difference(): +def boolean_operation(bool_type="DIFFERENCE"): ActiveObj = bpy.context.active_object + sel_index = 0 if bpy.context.selected_objects[0] != bpy.context.active_object else 1 - if bpy.context.selected_objects[0] != bpy.context.active_object: - bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY") - BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[0].name, "BOOLEAN") - BoolMod.object = bpy.context.selected_objects[0] - BoolMod.operation = "DIFFERENCE" - BoolMod.solver = bpy.context.scene.CarverSolver - bpy.context.selected_objects[0].draw_type = 'WIRE' - else: - BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[1].name, "BOOLEAN") - BoolMod.object = bpy.context.selected_objects[1] - BoolMod.operation = "DIFFERENCE" - BoolMod.solver = bpy.context.scene.CarverSolver - bpy.context.selected_objects[1].draw_type = 'WIRE' - - -def boolean_union(): - ActiveObj = bpy.context.active_object - - if bpy.context.selected_objects[0] != bpy.context.active_object: - BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[0].name, "BOOLEAN") - BoolMod.object = bpy.context.selected_objects[0] - BoolMod.operation = "UNION" - bpy.context.selected_objects[0].draw_type = 'WIRE' - else: - BoolMod = ActiveObj.modifiers.new("CT_" + bpy.context.selected_objects[1].name, "BOOLEAN") - BoolMod.object = bpy.context.selected_objects[1] - BoolMod.operation = "UNION" - bpy.context.selected_objects[1].draw_type = 'WIRE' + bpy.ops.object.modifier_apply(apply_as='DATA', modifier="CT_SOLIDIFY") + BoolMod = ActiveObj.modifiers.new( + "CT_" + bpy.context.selected_objects[sel_index].name, + "BOOLEAN" + ) + BoolMod.object = bpy.context.selected_objects[sel_index] + BoolMod.operation = bool_type + bpy.context.selected_objects[sel_index].draw_type = 'WIRE' def Rebool(context, self): - SelObj_Name = [] - BoolObj = [] - LastObj = context.active_object Brush = context.selected_objects[0] @@ -2204,12 +2113,10 @@ def Rebool(context, self): m = LastObjectCreated.modifiers.new("CT_INTERSECT", "BOOLEAN") m.operation = "INTERSECT" - m.solver = context.scene.CarverSolver m.object = Brush m = obj.modifiers.new("CT_DIFFERENCE", "BOOLEAN") m.operation = "DIFFERENCE" - m.solver = context.scene.CarverSolver m.object = Brush for mb in LastObj.modifiers: @@ -2262,14 +2169,14 @@ def createMeshFromData(self): if "CT_Profil" not in bpy.data.objects: ob = bpy.data.objects.new("CT_Profil", bpy.data.meshes[self.Profils[self.nProfil][0]]) - ob.location = mathutils.Vector((0.0, 0.0, 0.0)) + ob.location = Vector((0.0, 0.0, 0.0)) # Link object to scene and make active scn = bpy.context.scene scn.objects.link(ob) scn.objects.active = ob ob.select = True - ob.location = mathutils.Vector((10000.0, 0.0, 0.0)) + ob.location = Vector((10000.0, 0.0, 0.0)) ob.draw_type = "WIRE" self.SolidifyPossible = True @@ -2277,25 +2184,38 @@ def createMeshFromData(self): bpy.data.objects["CT_Profil"].data = bpy.data.meshes[self.Profils[self.nProfil][0]] +def Selection_Save_Restore(self): + if "CT_Profil" in bpy.data.objects: + Selection_Save(self) + bpy.ops.object.select_all(action='DESELECT') + bpy.data.objects["CT_Profil"].select = True + bpy.context.scene.objects.active = bpy.data.objects["CT_Profil"] + if bpy.data.objects["CT_Profil"] in self.SavSel: + self.SavSel.remove(bpy.data.objects["CT_Profil"]) + bpy.ops.object.delete(use_global=False) + Selection_Restore(self) + + def Selection_Save(self): + obj_name = getattr(bpy.context.active_object, "name", None) self.SavSel = bpy.context.selected_objects.copy() - self.Sav_ac = bpy.context.active_object + self.Sav_ac = obj_name def Selection_Restore(self): for o in self.SavSel: o.select = True - bpy.context.scene.objects.active = self.Sav_ac + if self.Sav_ac: + bpy.context.scene.objects.active = bpy.data.objects.get(self.Sav_ac, None) # Modal Operator class Carver(bpy.types.Operator): bl_idname = "object.carver" bl_label = "Carver" - bl_description = "Cut or create in object mode" + bl_description = "Cut or create Meshes in Object mode" bl_options = {'REGISTER', 'UNDO'} - # -------------------------------------------------------------------------------------------------- @classmethod def poll(cls, context): ob = None @@ -2306,14 +2226,12 @@ def poll(cls, context): (ob and ob.type == 'MESH' and context.mode == 'OBJECT') or (context.mode == 'OBJECT' and ob is None) or (context.mode == 'EDIT_MESH')) - # -------------------------------------------------------------------------------------------------- - # -------------------------------------------------------------------------------------------------- def modal(self, context, event): PI = 3.14156 - region_types = {'WINDOW', 'UI'} win = context.window + for area in win.screen.areas: if area.type in ('VIEW_3D'): for region in area.regions: @@ -2327,14 +2245,10 @@ def modal(self, context, event): return {'PASS_THROUGH'} try: # [Shift] - self.shift = False - if event.shift: - self.shift = True + self.shift = True if event.shift else False # [Ctrl] - self.ctrl = False - if event.ctrl: - self.ctrl = True + self.ctrl = True if event.ctrl else False # [Alt] self.alt = False @@ -2349,7 +2263,7 @@ def modal(self, context, event): self.alt = True # [Alt] release if self.InitPosition and self.alt is False: - # Update coordonnee + # Update coordinates for i in range(0, len(self.mouse_path)): l = list(self.mouse_path[i]) l[0] += self.xpos @@ -2417,15 +2331,14 @@ def modal(self, context, event): self.CreateGeometry() bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') # Cursor Snap - context.scene.DepthCursor = self.snapCursor + context.scene.mesh_carver.DepthCursor = self.snapCursor # Object Instantiate - context.scene.OInstanciate = self.Instantiate + context.scene.mesh_carver.OInstanciate = self.Instantiate # Random rotation - context.scene.ORandom = self.RandomRotation + context.scene.mesh_carver.ORandom = self.RandomRotation return {'FINISHED'} else: - # Cut self.Cut() UndoListUpdate(self) @@ -2459,28 +2372,13 @@ def modal(self, context, event): self.BrushSolidify = False self.CList = self.OB_List - if "CT_Profil" in bpy.data.objects: - Selection_Save(self) - bpy.ops.object.select_all(action='DESELECT') - bpy.data.objects["CT_Profil"].select = True - context.scene.objects.active = bpy.data.objects["CT_Profil"] - bpy.ops.object.delete(use_global=False) - Selection_Restore(self) - - context.scene.nProfile = self.nProfil - + Selection_Save_Restore(self) + context.scene.mesh_carver.nProfile = self.nProfil else: self.ObjectMode = False else: self.BrushSolidify = False - - if "CT_Profil" in bpy.data.objects: - Selection_Save(self) - bpy.ops.object.select_all(action='DESELECT') - bpy.data.objects["CT_Profil"].select = True - context.scene.objects.active = bpy.data.objects["CT_Profil"] - bpy.ops.object.delete(use_global=False) - Selection_Restore(self) + Selection_Save_Restore(self) if self.ProfileMode: createMeshFromData(self) @@ -2496,7 +2394,6 @@ def modal(self, context, event): bpy.ops.object.modifier_add(type='SOLIDIFY') context.object.modifiers["Solidify"].name = "CT_SOLIDIFY" - context.object.modifiers["CT_SOLIDIFY"].thickness = 0.1 Selection_Restore(self) @@ -2525,7 +2422,6 @@ def modal(self, context, event): Selection_Restore(self) else: - if self.Solidify_Active_Start: Selection_Save(self) self.BrushSolidify = True @@ -2556,12 +2452,6 @@ def modal(self, context, event): if event.type == context.scene.Key_Apply and event.value == 'PRESS': self.DontApply = not self.DontApply - if event.type == context.scene.Key_Solver and event.value == 'PRESS': - if context.scene.CarverSolver == "CARVE": - context.scene.CarverSolver = "BMESH" - else: - context.scene.CarverSolver = "CARVE" - # Scale object if event.type == context.scene.Key_Scale and event.value == 'PRESS': if self.ObjectScale is False: @@ -2731,7 +2621,6 @@ def modal(self, context, event): self.ProfileBrush.scale.x -= float(self.ascale) / 150.0 self.ProfileBrush.scale.y -= float(self.ascale) / 150.0 self.ProfileBrush.scale.z -= float(self.ascale) / 150.0 - else: if self.LMB: if self.ctrl: @@ -2746,10 +2635,9 @@ def modal(self, context, event): vBack = Pick(context, event, self) if vBack[0] is not None: self.ShowCursor = True - NormalObject = mathutils.Vector((0.0, 0.0, 1.0)) + NormalObject = Vector((0.0, 0.0, 1.0)) qR = RBenVe(NormalObject, vBack[1]) self.qRot = vBack[3] * qR - Pos = vBack[0] MoveCursor(qR, vBack[0], self) self.SavCurLoc = vBack[0] if self.ctrl: @@ -2758,13 +2646,13 @@ def modal(self, context, event): yEcart = abs(self.SavMousePos.y - self.SavCurLoc.y) zEcart = abs(self.SavMousePos.z - self.SavCurLoc.z) if (xEcart > yEcart) and (xEcart > zEcart): - self.CurLoc = mathutils.Vector( + self.CurLoc = Vector( (vBack[0].x, self.SavMousePos.y, self.SavMousePos.z)) if (yEcart > xEcart) and (yEcart > zEcart): - self.CurLoc = mathutils.Vector( + self.CurLoc = Vector( (self.SavMousePos.x, vBack[0].y, self.SavMousePos.z)) if (zEcart > xEcart) and (zEcart > yEcart): - self.CurLoc = mathutils.Vector( + self.CurLoc = Vector( (self.SavMousePos.x, self.SavMousePos.y, vBack[0].z)) else: self.CurLoc = vBack[0] @@ -2807,7 +2695,7 @@ def modal(self, context, event): if self.LMB is False: vBack = Pick(context, event, self) if vBack[0] is not None: - NormalObject = mathutils.Vector((0.0, 0.0, 1.0)) + NormalObject = Vector((0.0, 0.0, 1.0)) self.aqR = RBenVe(NormalObject, vBack[1]) self.qRot = vBack[3] * self.aqR self.am = event.mouse_region_x, event.mouse_region_y @@ -2871,13 +2759,13 @@ def modal(self, context, event): self.CreateGeometry() bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') # Depth Cursor - context.scene.DepthCursor = self.snapCursor + context.scene.mesh_carver.DepthCursor = self.snapCursor # Instantiate object - context.scene.OInstanciate = self.Instantiate + context.scene.mesh_carver.OInstanciate = self.Instantiate # Random rotation - context.scene.ORandom = self.RandomRotation + context.scene.mesh_carver.ORandom = self.RandomRotation # Apply operation - context.scene.DontApply = self.DontApply + context.scene.mesh_carver.DontApply = self.DontApply # if Object mode, set intiale state if self.ObjectBrush is not None: @@ -2900,14 +2788,12 @@ def modal(self, context, event): Selection_Restore(self) - context.scene.nProfile = self.nProfil + context.scene.mesh_carver.nProfile = self.nProfil return {'FINISHED'} else: - # Cut self.Cut() UndoListUpdate(self) - else: # Line self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) @@ -2941,17 +2827,16 @@ def modal(self, context, event): else: if self.step > 0: self.step -= 1 - # Quit elif event.type in {'RIGHTMOUSE', 'ESC'}: # Depth Cursor - context.scene.DepthCursor = self.snapCursor + context.scene.mesh_carver.DepthCursor = self.snapCursor # Instantiate object - context.scene.OInstanciate = self.Instantiate + context.scene.mesh_carver.OInstanciate = self.Instantiate # Random Rotation - context.scene.ORandom = self.RandomRotation + context.scene.mesh_carver.ORandom = self.RandomRotation # Apply boolean operation - context.scene.DontApply = self.DontApply + context.scene.mesh_carver.DontApply = self.DontApply # Reset Object if self.ObjectBrush is not None: @@ -2971,24 +2856,16 @@ def modal(self, context, event): context.scene.objects.active = self.ObjectBrush bpy.ops.object.modifier_remove(modifier="CT_SOLIDIFY") - bpy.ops.object.select_all(action='TOGGLE') Selection_Restore(self) - if "CT_Profil" in bpy.data.objects: - Selection_Save(self) - bpy.ops.object.select_all(action='DESELECT') - bpy.data.objects["CT_Profil"].select = True - context.scene.objects.active = bpy.data.objects["CT_Profil"] - bpy.ops.object.delete(use_global=False) - Selection_Restore(self) - + Selection_Save_Restore(self) context.scene.objects.active = self.CurrentActive - - context.scene.nProfile = self.nProfil + context.scene.mesh_carver.nProfile = self.nProfil bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') + # Remove Copy Object Brush if bpy.data.objects.get("CarverBrushCopy") is not None: brush = bpy.data.objects["CarverBrushCopy"] @@ -3003,7 +2880,6 @@ def modal(self, context, event): except: print("\n[Carver MT ERROR]\n") - import traceback traceback.print_exc() @@ -3016,242 +2892,246 @@ def modal(self, context, event): return {'FINISHED'} - # -------------------------------------------------------------------------------------------------- + def cancel(self, context): + # Note: used to prevent memory leaks on quiting Blender while the modal operator + # is still running, gets called on return {"CANCELLED"} + bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') - # -------------------------------------------------------------------------------------------------- def invoke(self, context, event): - if context.area.type == 'VIEW_3D': - if context.mode == 'EDIT_MESH': - bpy.ops.object.mode_set(mode='OBJECT') - - win = context.window - - # Get default patterns - self.Profils = [] - for p in Profils: - self.Profils.append((p[0], p[1], p[2], p[3])) + if context.area.type != 'VIEW_3D': + self.report({'WARNING'}, + "View3D not found or not currently active. Operation Cancelled") + return {'CANCELLED'} - for o in context.scene.objects: - if not o.name.startswith(context.scene.ProfilePrefix): - continue + if context.mode == 'EDIT_MESH': + bpy.ops.object.mode_set(mode='OBJECT') - # In-scene profiles may have changed, remove them to refresh - for m in bpy.data.meshes: - if m.name.startswith(context.scene.ProfilePrefix): - bpy.data.meshes.remove(m) + # test if some other object types are selected that are not meshes + test_selection = True + for obj in context.selected_objects: + if obj.type != "MESH": + test_selection = False + break + if not test_selection: + self.report({'WARNING'}, + "Some selected objects are not of the Mesh type. Operation Cancelled") + return {"CANCELLED"} + + # Get default patterns + self.Profils = [] + for p in Profils: + self.Profils.append((p[0], p[1], p[2], p[3])) + + for o in context.scene.objects: + if not o.name.startswith(context.scene.ProfilePrefix): + continue + + # In-scene profiles may have changed, remove them to refresh + for m in bpy.data.meshes: + if m.name.startswith(context.scene.ProfilePrefix): + bpy.data.meshes.remove(m) + + vertices = [] + for v in o.data.vertices: + vertices.append((v.co.x, v.co.y, v.co.z)) + + faces = [] + for f in o.data.polygons: + face = [] + for v in f.vertices: + face.append(v) + + faces.append(face) + + self.Profils.append( + (o.name, + Vector((o.location.x, o.location.y, o.location.z)), + vertices, faces) + ) - vertices = [] - for v in o.data.vertices: - vertices.append((v.co.x, v.co.y, v.co.z)) + self.nProfil = context.scene.mesh_carver.nProfile + self.MaxProfil = len(self.Profils) - faces = [] - for f in o.data.polygons: - face = [] + # reset selected profile if last profile exceeds length of array + if self.nProfil >= self.MaxProfil: + self.nProfil = context.scene.mesh_carver.nProfile = 0 - for v in f.vertices: - face.append(v) + # Save selection + self.CurrentSelection = context.selected_objects.copy() + self.CurrentActive = context.active_object + self.SavSel = context.selected_objects.copy() + self.Sav_ac = None - faces.append(face) + args = (self, context) - self.Profils.append( - (o.name, - mathutils.Vector((o.location.x, o.location.y, o.location.z)), - vertices, faces) - ) + self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') - self.nProfil = context.scene.nProfile - self.MaxProfil = len(self.Profils) + self.mouse_path = [(0, 0), (0, 0)] - # reset selected profile if last profile exceeds length of array - if self.nProfil >= self.MaxProfil: - self.nProfil = context.scene.nProfile = 0 + self.shift = False + self.ctrl = False + self.alt = False + self.bDone = False - # Save selection - self.CurrentSelection = context.selected_objects.copy() - self.CurrentActive = context.active_object - self.SavSel = context.selected_objects.copy() - self.Sav_ac = None + self.DontApply = context.scene.mesh_carver.DontApply + self.Auto_BevelUpdate = True - args = (self, context) + # Cut type (Rectangle, Circle, Line) + self.CutMode = 0 + self.BoolOps = DIFFERENCE - self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL') + # Circle variables + self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90] + self.step = 4 + self.stepRotation = 0 - self.mouse_path = [(0, 0), (0, 0)] + # Primitives Position + self.xpos = 0 + self.ypos = 0 + self.InitPosition = False - self.shift = False - self.ctrl = False - self.alt = False + # Line Increment + self.Increment = 15 + # Close polygonal shape + self.Closed = False - self.bDone = False + # Depth Cursor + self.snapCursor = context.scene.mesh_carver.DepthCursor - self.DontApply = context.scene.DontApply - self.Auto_BevelUpdate = True + # Help + self.AskHelp = False - # Cut type (Rectangle, Circle, Line) - self.CutMode = 0 - self.BoolOps = DIFFERENCE + # Working object + self.OpsObj = context.active_object - # Circle variables - self.stepAngle = [2, 4, 5, 6, 9, 10, 15, 20, 30, 40, 45, 60, 72, 90] - self.step = 4 - self.stepRotation = 0 + # Create mode + self.CreateMode = False + self.ExclusiveCreateMode = False + if len(context.selected_objects) == 0: + self.ExclusiveCreateMode = True + self.CreateMode = True - # Primitives Position - self.xpos = 0 - self.ypos = 0 - self.InitPosition = False + # Rebool forced (cut line) + self.ForceRebool = False - # Line Increment - self.Increment = 15 - # Close polygonal shape - self.Closed = False - - # Depth Cursor - self.snapCursor = context.scene.DepthCursor - - # Help - self.AskHelp = False - - # Working object - self.OpsObj = context.active_object - - # Create mode - self.CreateMode = False - self.ExclusiveCreateMode = False - if len(context.selected_objects) == 0: - self.ExclusiveCreateMode = True - self.CreateMode = True - - # Rebool forced (cut line) - self.ForceRebool = False - - self.ViewVector = mathutils.Vector() - - self.CurrentObj = None - - # Brush - self.BrushSolidify = False - self.WidthSolidify = False - self.CarveDepth = False - self.BrushDepth = False - self.BrushDepthOffset = 0.0 - - self.ObjectScale = False - - self.CircleListRaw = [] - self.CLR_C = [] - self.CurLoc = mathutils.Vector((0.0, 0.0, 0.0)) - self.SavCurLoc = mathutils.Vector((0.0, 0.0, 0.0)) - self.CRadius = 1.0 - CreatePrimitive(self, 10.0, 1.0) - self.VertsList = [] - self.FacesList = [] - - self.am = -1, -1 - self.SavMousePos = None - self.xSavMouse = 0 - - self.ascale = 0 - self.aRotZ = 0 - self.nRotZ = 0 - self.aqR = None - self.qRot = None - - self.RandomRotation = context.scene.ORandom - - self.ShowCursor = True - - self.ObjectMode = False - self.ProfileMode = False - self.Instantiate = context.scene.OInstanciate - - self.ProfileBrush = None - self.ObjectBrush = None - self.InitBrushPosition = None - self.InitBrushScale = None - self.InitBrushQRotation = None - self.InitBrushERotation = None - self.InitBrushARotation = None - - self.ObjectBrush_DT = "WIRE" - self.XRay = False - - # Grid mesh - self.nbcol = 1 - self.nbrow = 1 - self.gapx = 0 - self.gapy = 0 - self.scale_x = 1 - self.scale_y = 1 - - self.GridScaleX = False - self.GridScaleY = False - - if len(context.selected_objects) > 1: - self.ObjectBrush = context.active_object - - # Copy the brush object - ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy()) - ob.location = self.ObjectBrush.location - scene = context.scene - scene.objects.link(ob) - scene.update() - - # Get default variables - self.InitBrushPosition = self.ObjectBrush.location.copy() - self.InitBrushScale = self.ObjectBrush.scale.copy() - self.InitBrushQRotation = self.ObjectBrush.rotation_quaternion.copy() - self.InitBrushERotation = self.ObjectBrush.rotation_euler.copy() - self.ObjectBrush_DT = self.ObjectBrush.draw_type - self.XRay = self.ObjectBrush.show_x_ray - # Test if flat object - z = self.ObjectBrush.data.vertices[0].co.z - ErrorMarge = 0.01 - self.Solidify_Active_Start = True - for v in self.ObjectBrush.data.vertices: - if abs(v.co.z - z) > ErrorMarge: - self.Solidify_Active_Start = False - break - self.SolidifyPossible = False - - self.CList = [] - self.OPList = [] - self.RList = [] - self.OB_List = [] - - for ent in context.selected_objects: - if ent != self.ObjectBrush: - self.OB_List.append(ent) - - # Left button - self.LMB = False - - # Undo Variables - self.undo_index = 0 - self.undo_limit = context.user_preferences.edit.undo_steps - self.undo_list = [] - - # Boolean operations type - self.BooleanType = 0 - - self.UList = [] - self.UList_Index = -1 - self.UndoOps = [] - - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} - else: - self.report({'WARNING'}, "View3D not found, cannot run operator") - return {'CANCELLED'} - # -------------------------------------------------------------------------------------------------- + self.ViewVector = Vector() + self.CurrentObj = None + + # Brush + self.BrushSolidify = False + self.WidthSolidify = False + self.CarveDepth = False + self.BrushDepth = False + self.BrushDepthOffset = 0.0 + + self.ObjectScale = False + + self.CircleListRaw = [] + self.CLR_C = [] + self.CurLoc = Vector((0.0, 0.0, 0.0)) + self.SavCurLoc = Vector((0.0, 0.0, 0.0)) + self.CRadius = 1.0 + CreatePrimitive(self, 10.0, 1.0) + self.VertsList = [] + self.FacesList = [] + + self.am = -1, -1 + self.SavMousePos = None + self.xSavMouse = 0 + + self.ascale = 0 + self.aRotZ = 0 + self.nRotZ = 0 + self.aqR = None + self.qRot = None + + self.RandomRotation = context.scene.mesh_carver.ORandom + + self.ShowCursor = True + self.ObjectMode = False + self.ProfileMode = False + self.Instantiate = context.scene.mesh_carver.OInstanciate + + self.ProfileBrush = None + self.ObjectBrush = None + self.InitBrushPosition = None + self.InitBrushScale = None + self.InitBrushQRotation = None + self.InitBrushERotation = None + self.InitBrushARotation = None + + self.ObjectBrush_DT = "WIRE" + self.XRay = False + + # Grid mesh + self.nbcol = 1 + self.nbrow = 1 + self.gapx = 0 + self.gapy = 0 + self.scale_x = 1 + self.scale_y = 1 + + self.GridScaleX = False + self.GridScaleY = False + + if len(context.selected_objects) > 1: + self.ObjectBrush = context.active_object + + # Copy the brush object + ob = bpy.data.objects.new("CarverBrushCopy", context.object.data.copy()) + ob.location = self.ObjectBrush.location + scene = context.scene + scene.objects.link(ob) + scene.update() + + # Get default variables + self.InitBrushPosition = self.ObjectBrush.location.copy() + self.InitBrushScale = self.ObjectBrush.scale.copy() + self.InitBrushQRotation = self.ObjectBrush.rotation_quaternion.copy() + self.InitBrushERotation = self.ObjectBrush.rotation_euler.copy() + self.ObjectBrush_DT = self.ObjectBrush.draw_type + self.XRay = self.ObjectBrush.show_x_ray + # Test if flat object + z = self.ObjectBrush.data.vertices[0].co.z + ErrorMarge = 0.01 + self.Solidify_Active_Start = True + for v in self.ObjectBrush.data.vertices: + if abs(v.co.z - z) > ErrorMarge: + self.Solidify_Active_Start = False + break + self.SolidifyPossible = False + + self.CList = [] + self.OPList = [] + self.RList = [] + self.OB_List = [] + + for ent in context.selected_objects: + if ent != self.ObjectBrush: + self.OB_List.append(ent) + + # Left button + self.LMB = False + + # Undo Variables + self.undo_index = 0 + self.undo_limit = context.user_preferences.edit.undo_steps + self.undo_list = [] + + # Boolean operations type + self.BooleanType = 0 + + self.UList = [] + self.UList_Index = -1 + self.UndoOps = [] + + context.window_manager.modal_handler_add(self) + return {'RUNNING_MODAL'} - # -------------------------------------------------------------------------------------------------- def CreateGeometry(self): context = bpy.context - - region_id = context.region.id - bLocalView = False + for area in context.screen.areas: if area.type == 'VIEW_3D': if area.spaces[0].local_view is not None: @@ -3309,14 +3189,10 @@ def CreateGeometry(self): self.bDone = False self.mouse_path.clear() self.mouse_path = [(0, 0), (0, 0)] - # -------------------------------------------------------------------------------------------------- - # -------------------------------------------------------------------------------------------------- def Cut(self): context = bpy.context - UNDO = [] - # Local view ? bLocalView = False for area in context.screen.areas: @@ -3358,7 +3234,7 @@ def Cut(self): bpy.ops.mesh.normals_make_consistent() bpy.ops.object.mode_set(mode='OBJECT') else: - # Create liste + # Create list if self.ObjectMode: for o in self.CurrentSelection: if o != self.ObjectBrush: @@ -3380,7 +3256,7 @@ def Cut(self): if len(context.selected_objects) > 0: bpy.ops.object.select_all(action='TOGGLE') - # Testif intitiale object has bevel + # Test if initial object has bevel BevelAO = False for obj in ActiveObjList: for mb in obj.modifiers: @@ -3407,14 +3283,11 @@ def Cut(self): if (self.shift is False) and (self.ForceRebool is False): if self.ObjectMode or self.ProfileMode: if self.BoolOps == UNION: - # Union - boolean_union() + boolean_operation(bool_type="UNION") else: - # Cut object - boolean_difference() + boolean_operation(bool_type="DIFFERENCE") else: - # Cut - boolean_difference() + boolean_operation(bool_type="DIFFERENCE") # Apply booleans if self.DontApply is False: @@ -3487,7 +3360,6 @@ def Cut(self): if len(context.selected_objects) > 0: bpy.ops.object.select_all(action='TOGGLE') bpy.data.objects[self.CurrentObj.name].select = True - cname = self.CurrentObj.name bpy.ops.object.delete(use_global=False) else: if self.ObjectMode: @@ -3496,7 +3368,7 @@ def Cut(self): if len(context.selected_objects) > 0: bpy.ops.object.select_all(action='TOGGLE') - # Select cutted objects + # Select cut objects for obj in lastSelected: bpy.data.objects[obj.name].select = True @@ -3508,10 +3380,10 @@ def Cut(self): if self.Auto_BevelUpdate: update_bevel(context) - # Reselect intiale objects + # Re-select initial objects bpy.ops.object.select_all(action='TOGGLE') if self.ObjectMode: - # Reselect brush + # Re-select brush self.ObjectBrush.select = True for ActiveObj in ActiveObjList: bpy.data.objects[ActiveObj.name].select = True @@ -3535,23 +3407,45 @@ def Cut(self): self.ForceRebool = False -classes = ( - Carver, +class CarverProperties(bpy.types.PropertyGroup): + DepthCursor = BoolProperty( + name="DepthCursor", + default=False + ) + OInstanciate = BoolProperty( + name="Obj_Instantiate", + default=False + ) + ORandom = BoolProperty( + name="Random_Rotation", + default=False + ) + DontApply = BoolProperty( + name="Dont_Apply", + default=False ) + nProfile = IntProperty( + name="Num_Profile", + default=0 + ) + addon_keymaps = [] +classes = ( + CarverPrefs, + CarverProperties, + Carver, +) -def register(): - bpy.types.Scene.DepthCursor = BoolProperty(name="DepthCursor", default=False) - bpy.types.Scene.OInstanciate = BoolProperty(name="Obj_Instantiate", default=False) - bpy.types.Scene.ORandom = BoolProperty(name="Random_Rotation", default=False) - bpy.types.Scene.DontApply = BoolProperty(name="Dont_Apply", default=False) - bpy.types.Scene.nProfile = IntProperty(name="Num_Profile", default=0) - bpy.utils.register_class(CarverPrefs) +def register(): + for cls in classes: + bpy.utils.register_class(cls) - bpy.utils.register_class(Carver) + bpy.types.Scene.mesh_carver = PointerProperty( + type=CarverProperties + ) # add keymap entry kcfg = bpy.context.window_manager.keyconfigs.addon if kcfg: @@ -3561,14 +3455,15 @@ def register(): def unregister(): - bpy.utils.unregister_class(CarverPrefs) + for cls in classes: + bpy.utils.unregister_class(cls) # remove keymap entry for km, kmi in addon_keymaps: km.keymap_items.remove(kmi) addon_keymaps.clear() - bpy.utils.unregister_class(Carver) + del bpy.types.Scene.mesh_carver if __name__ == "__main__": diff --git a/release/scripts/addons/mesh_extra_tools/mesh_offset_edges.py b/release/scripts/addons/mesh_extra_tools/mesh_offset_edges.py index 88bdb1716994..2168e76cfb0e 100644 --- a/release/scripts/addons/mesh_extra_tools/mesh_offset_edges.py +++ b/release/scripts/addons/mesh_extra_tools/mesh_offset_edges.py @@ -646,7 +646,7 @@ def draw(self, context): row.prop(self, 'edge_rail_only_end', text="OnlyEnd", toggle=True) layout.prop(self, 'mirror_modifier') - # layout.operator('mesh.offset_edges', text="Repeat") # for 2.79a, Crashing... + layout.operator('mesh.offset_edges', text="Repeat") if self.follow_face: layout.separator() diff --git a/release/scripts/addons/mesh_tiny_cad/BIX.py b/release/scripts/addons/mesh_tiny_cad/BIX.py index faac521271b4..89908f607ecf 100644 --- a/release/scripts/addons/mesh_tiny_cad/BIX.py +++ b/release/scripts/addons/mesh_tiny_cad/BIX.py @@ -92,11 +92,3 @@ def poll(cls, context): def execute(self, context): add_line_to_bisection(self) return {'FINISHED'} - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/CCEN.py b/release/scripts/addons/mesh_tiny_cad/CCEN.py index f625504b9c8d..ef2b5eae49d5 100644 --- a/release/scripts/addons/mesh_tiny_cad/CCEN.py +++ b/release/scripts/addons/mesh_tiny_cad/CCEN.py @@ -157,11 +157,3 @@ def poll(cls, context): def execute(self, context): dispatch(context, mode=1) return {'FINISHED'} - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/CFG.py b/release/scripts/addons/mesh_tiny_cad/CFG.py index ed703a2f9078..55e0ff9fabac 100644 --- a/release/scripts/addons/mesh_tiny_cad/CFG.py +++ b/release/scripts/addons/mesh_tiny_cad/CFG.py @@ -74,11 +74,3 @@ def unregister_icons(): for pcoll in icon_collection.values(): bpy.utils.previews.remove(pcoll) icon_collection.clear() - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/E2F.py b/release/scripts/addons/mesh_tiny_cad/E2F.py index 8c95f126dd81..25f17e9f2489 100644 --- a/release/scripts/addons/mesh_tiny_cad/E2F.py +++ b/release/scripts/addons/mesh_tiny_cad/E2F.py @@ -100,11 +100,3 @@ def poll(cls, context): def execute(self, context): extend_vertex(self) return {'FINISHED'} - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/V2X.py b/release/scripts/addons/mesh_tiny_cad/V2X.py index c230f4a2c4be..c8683297d292 100644 --- a/release/scripts/addons/mesh_tiny_cad/V2X.py +++ b/release/scripts/addons/mesh_tiny_cad/V2X.py @@ -60,11 +60,3 @@ def poll(cls, context): def execute(self, context): add_vertex_to_intersection() return {'FINISHED'} - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/VTX.py b/release/scripts/addons/mesh_tiny_cad/VTX.py index 5ad035b28abf..5cdb2fcfe0c7 100644 --- a/release/scripts/addons/mesh_tiny_cad/VTX.py +++ b/release/scripts/addons/mesh_tiny_cad/VTX.py @@ -173,11 +173,3 @@ def execute(self, context): bmesh.update_edit_mesh(me, True) return {'FINISHED'} - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/XALL.py b/release/scripts/addons/mesh_tiny_cad/XALL.py index 83ecdb52522f..fc0ed76e8f14 100644 --- a/release/scripts/addons/mesh_tiny_cad/XALL.py +++ b/release/scripts/addons/mesh_tiny_cad/XALL.py @@ -174,11 +174,3 @@ def execute(self, context): print('must be in edit mode') return {'FINISHED'} - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) diff --git a/release/scripts/addons/mesh_tiny_cad/__init__.py b/release/scripts/addons/mesh_tiny_cad/__init__.py index 611c67c4c08c..4a9a62f06e90 100644 --- a/release/scripts/addons/mesh_tiny_cad/__init__.py +++ b/release/scripts/addons/mesh_tiny_cad/__init__.py @@ -50,7 +50,7 @@ import bpy -from .CFG import TinyCADProperties +from .CFG import TinyCADProperties, VIEW3D_MT_edit_mesh_tinycad from .CFG import register_icons, unregister_icons from . import VTX, V2X, XALL, BIX, CCEN, E2F @@ -59,10 +59,20 @@ def menu_func(self, context): self.layout.menu("VIEW3D_MT_edit_mesh_tinycad") self.layout.separator() +classes = [ + TinyCADProperties, VIEW3D_MT_edit_mesh_tinycad, + VTX.TCAutoVTX, + XALL.TCIntersectAllEdges, + V2X.TCVert2Intersection, + E2F.TCEdgeToFace, + CCEN.TCCallBackCCEN, CCEN.TCCircleCenter, + BIX.TCLineOnBisection +] def register(): register_icons() - bpy.utils.register_module(__name__) + for cls in classes: + bpy.utils.register_class(cls) bpy.types.Scene.tinycad_props = bpy.props.PointerProperty( name="TinyCAD props", type=TinyCADProperties) bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func) @@ -70,6 +80,7 @@ def register(): def unregister(): bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func) - bpy.utils.unregister_module(__name__) + for cls in reversed(classes): + bpy.utils.unregister_class(cls) del bpy.types.Scene.tinycad_props unregister_icons() diff --git a/release/scripts/addons/mesh_tissue/__init__.py b/release/scripts/addons/mesh_tissue/__init__.py index bef996256ab6..68921506ea16 100644 --- a/release/scripts/addons/mesh_tissue/__init__.py +++ b/release/scripts/addons/mesh_tissue/__init__.py @@ -33,7 +33,7 @@ bl_info = { "name": "Tissue", "author": "Alessandro Zomparelli (Co-de-iT)", - "version": (0, 3, 3), + "version": (0, 3, 4), "blender": (2, 7, 9), "location": "", "description": "Tools for Computational Design", diff --git a/release/scripts/addons/mesh_tissue/colors_groups_exchanger.py b/release/scripts/addons/mesh_tissue/colors_groups_exchanger.py index b102a5b60abb..6a4ab1a9b2e7 100644 --- a/release/scripts/addons/mesh_tissue/colors_groups_exchanger.py +++ b/release/scripts/addons/mesh_tissue/colors_groups_exchanger.py @@ -36,7 +36,7 @@ bl_info = { "name": "Colors/Groups Exchanger", "author": "Alessandro Zomparelli (Co-de-iT)", - "version": (0, 3, 1), + "version": (0, 3, 2), "blender": (2, 7, 8), "location": "", "description": ("Convert vertex colors channels to vertex groups and vertex" @@ -99,6 +99,11 @@ def execute(self, context): id_blue = ids id_value = ids + # Fix T53350: Access of h, s, v values has changed since 2.79 release + # use rgb_to_hsv function to get the v component + blender_version = bool(bpy.app.version >= (2, 79, 1)) + from colorsys import rgb_to_hsv + boolCol = len(obj.data.vertex_colors) if boolCol: col_name = obj.data.vertex_colors.active.name @@ -130,11 +135,10 @@ def execute(self, context): obj.vertex_groups[id_value].name = col_name + '_value' ids += 1 - mult = 1 - if self.invert: - mult = -1 + mult = -1 if self.invert else 1 bpy.ops.object.mode_set(mode='OBJECT') + sub_red = 1 + self.value + self.blue + self.green sub_green = 1 + self.value + self.blue sub_blue = 1 + self.value @@ -150,19 +154,24 @@ def execute(self, context): gr = obj.data.vertices[v].groups if self.red: gr[min(len(gr) - sub_red, id_red)].weight = \ - self.invert + mult * v_colors[i].color.r + self.invert + mult * v_colors[i].color[0] if self.green: gr[min(len(gr) - sub_green, id_green)].weight = \ - self.invert + mult * v_colors[i].color.g + self.invert + mult * v_colors[i].color[1] if self.blue: gr[min(len(gr) - sub_blue, id_blue)].weight = \ - self.invert + mult * v_colors[i].color.b + self.invert + mult * v_colors[i].color[2] if self.value: + col_value = v_colors[i].color.v if not blender_version else \ + rgb_to_hsv(v_colors[i].color[0], + v_colors[i].color[0], + v_colors[i].color[0])[2] + gr[min(len(gr) - sub_value, id_value)].weight = \ - self.invert + mult * v_colors[i].color.v + self.invert + mult * col_value i += 1 bpy.ops.paint.weight_paint_toggle() @@ -262,6 +271,7 @@ def execute(self, context): i += 1 bpy.ops.paint.vertex_paint_toggle() bpy.context.object.data.vertex_colors[colors_id].active_render = True + return {'FINISHED'} @@ -314,6 +324,9 @@ class curvature_to_vertex_groups(Operator): ) def execute(self, context): + # Fix T53350: Access of r, g, b values has changed since 2.79.1 + blender_version = bool(bpy.app.version >= (2, 79, 1)) + bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.mesh.vertex_color_add() vertex_colors = bpy.context.active_object.data.vertex_colors @@ -322,9 +335,14 @@ def execute(self, context): vertex_colors[-1].name = "Curvature" for c in vertex_colors[-1].data: - c.color.r, c.color.g, c.color.b = 1, 1, 1 + if blender_version: + # white color, alpha 1 + c.color[0], c.color[1], c.color[2], c.color[3] = 1, 1, 1, 1 + else: + c.color.r, c.color.g, c.color.b = 1, 1, 1 bpy.ops.object.mode_set(mode='VERTEX_PAINT') + bpy.ops.paint.vertex_color_dirt( blur_strength=self.blur_strength, blur_iterations=self.blur_iterations, diff --git a/release/scripts/addons/mocap/mocap_constraints.py b/release/scripts/addons/mocap/mocap_constraints.py index 4509193ceaa4..d263dfad7b3f 100644 --- a/release/scripts/addons/mocap/mocap_constraints.py +++ b/release/scripts/addons/mocap/mocap_constraints.py @@ -385,13 +385,15 @@ def bakeAllConstraints(obj, s_frame, e_frame, bones): constraintStrip.frame_end = e_frame if selectedBones: # Use bake function from NLA Bake Action operator - anim_utils.bake_action(s_frame, - e_frame, - action=constraintStrip.action, - only_selected=True, - do_pose=True, - do_object=False, - ) + anim_utils.bake_action( + obj, + s_frame, + e_frame, + action=constraintStrip.action, + only_selected=True, + do_pose=True, + do_object=False, + ) if simpleBake: #Do a "simple" bake, location only, world space only. locBake(s_frame, e_frame, simpleBake) diff --git a/release/scripts/addons/mocap/retarget.py b/release/scripts/addons/mocap/retarget.py index 66c622800629..d0b203cd8359 100644 --- a/release/scripts/addons/mocap/retarget.py +++ b/release/scripts/addons/mocap/retarget.py @@ -521,7 +521,15 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): else: prepareForBake(enduser_obj) print("Retargeting pose (Advanced Retarget)") - bake_action(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False, frame_step=step) + bake_action( + bpy.context.object, + s_frame, e_frame, + action=enduser_obj.animation_data.action, + only_selected=True, + do_pose=True, + do_object=False, + frame_step=step, + ) name = performer_obj.animation_data.action.name[:10] #We trim the name down to 10 chars because of Action Name length maximum enduser_obj.animation_data.action.name = "Base " + name diff --git a/release/scripts/addons/modules/snap_context/__init__.py b/release/scripts/addons/modules/snap_context/__init__.py index 0c17b2cb37a6..77e84726f397 100644 --- a/release/scripts/addons/modules/snap_context/__init__.py +++ b/release/scripts/addons/modules/snap_context/__init__.py @@ -191,6 +191,11 @@ def _get_snap_obj_by_obj(self, obj): def __del__(self): if not self.freed: self._offscreen.free() + # Some objects may still be being referenced + for snap_obj in self.snap_objects: + del snap_obj.data + del snap_obj.mat + del snap_obj del self.snap_objects ## PUBLIC ## diff --git a/release/scripts/addons/modules/snap_context/mesh_drawing.py b/release/scripts/addons/modules/snap_context/mesh_drawing.py index c85da506f24c..bdfca4d366e4 100644 --- a/release/scripts/addons/modules/snap_context/mesh_drawing.py +++ b/release/scripts/addons/modules/snap_context/mesh_drawing.py @@ -158,12 +158,32 @@ def __del__(self): class GPU_Indices_Mesh(): shader = None + @classmethod + def end_opengl(cls): + del cls.shader + del cls._NULL + del cls.P + del cls.MV + del cls.MVP + del cls.vert_index + del cls.tri_co + del cls.edge_co + del cls.vert_co + + del cls + @classmethod def init_opengl(cls): # OpenGL was already initialized, nothing to do here. if cls.shader is not None: return + import atexit + + # Make sure we only registered the callback once. + atexit.unregister(cls.end_opengl) + atexit.register(cls.end_opengl) + cls.shader = Shader( load_shader('3D_vert.glsl'), None, @@ -173,6 +193,8 @@ def init_opengl(cls): cls.unif_use_clip_planes = bgl.glGetUniformLocation(cls.shader.program, 'use_clip_planes') cls.unif_clip_plane = bgl.glGetUniformLocation(cls.shader.program, 'clip_plane') + cls._NULL = gl_buffer_void_as_long(0) + cls.unif_MVP = bgl.glGetUniformLocation(cls.shader.program, 'MVP') cls.unif_MV = bgl.glGetUniformLocation(cls.shader.program, 'MV') cls.unif_offset = bgl.glGetUniformLocation(cls.shader.program, 'offset') @@ -182,6 +204,7 @@ def init_opengl(cls): cls.P = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) cls.MV = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) + cls.MVP = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) # returns of public API # cls.vert_index = bgl.Buffer(bgl.GL_INT, 1) @@ -193,10 +216,6 @@ def init_opengl(cls): def __init__(self, obj, draw_tris, draw_edges, draw_verts): GPU_Indices_Mesh.init_opengl() - self._NULL = gl_buffer_void_as_long(0) - - self.MVP = bgl.Buffer(bgl.GL_FLOAT, (4, 4)) - self.obj = obj self.draw_tris = draw_tris self.draw_edges = draw_edges @@ -427,8 +446,6 @@ def get_loosevert_index(self, index): def __del__(self): - del self._NULL - if self.vbo_tris: bgl.glDeleteBuffers(1, self.vbo_tris) bgl.glDeleteBuffers(1, self.vbo_tri_indices) diff --git a/release/scripts/addons/object_fracture/__init__.py b/release/scripts/addons/object_fracture/__init__.py index e01f774ab591..0ac9f403631a 100644 --- a/release/scripts/addons/object_fracture/__init__.py +++ b/release/scripts/addons/object_fracture/__init__.py @@ -57,7 +57,7 @@ def draw(self, context): def menu_func(self, context): - self.layout.menu("INFO_MT_add_fracture_objects", icon="PLUGIN") + self.layout.menu("INFO_MT_add_fracture_objects") def register(): diff --git a/release/scripts/addons/object_print3d_utils/__init__.py b/release/scripts/addons/object_print3d_utils/__init__.py index f665f54c0ef6..26d0c7ef86fc 100644 --- a/release/scripts/addons/object_print3d_utils/__init__.py +++ b/release/scripts/addons/object_print3d_utils/__init__.py @@ -21,7 +21,7 @@ bl_info = { "name": "3D Print Toolbox", "author": "Campbell Barton", - "blender": (2, 65, 0), + "blender": (2, 79, 0), "location": "3D View > Toolbox", "description": "Utilities for 3D printing", "warning": "", @@ -37,6 +37,8 @@ importlib.reload(operators) importlib.reload(mesh_helpers) else: + import math + import bpy from bpy.props import ( StringProperty, @@ -49,15 +51,14 @@ AddonPreferences, PropertyGroup, ) + from . import ( ui, operators, ) -import math - -class Print3DSettings(PropertyGroup): +class Print3D_Scene_Props(PropertyGroup): export_format = EnumProperty( name="Format", description="Format type to export to", @@ -118,17 +119,14 @@ class Print3DSettings(PropertyGroup): ) -# Add-ons Preferences Update Panel - -# Define Panel classes for updating +# Update panel category name panels = ( - ui.Print3DToolBarObject, - ui.Print3DToolBarMesh, + ui.VIEW3D_PT_Print3D_Object, + ui.VIEW3D_PT_Print3D_Mesh, ) -def update_panel(self, context): - message = "3D Print Toolbox: Updating Panel locations has failed" +def update_panels(self, context): try: for panel in panels: if "bl_rna" in panel.__dict__: @@ -139,61 +137,58 @@ def update_panel(self, context): bpy.utils.register_class(panel) except Exception as e: + message = "3D Print Toolbox: Updating Panel locations has failed" print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e)) - pass -class printpreferences(AddonPreferences): - # this must match the addon name, use '__package__' - # when defining this in a submodule of a python package. +class Print3D_Preferences(AddonPreferences): bl_idname = __name__ category = StringProperty( - name="Tab Category", - description="Choose a name for the category of the panel", - default="3D Printing", - update=update_panel - ) + name="Tab Category", + description="Choose a name for the category of the panel", + default="3D Printing", + update=update_panels, + ) def draw(self, context): layout = self.layout - row = layout.row() - col = row.column() + col = layout.column() col.label(text="Tab Category:") col.prop(self, "category", text="") classes = ( - ui.Print3DToolBarObject, - ui.Print3DToolBarMesh, + ui.VIEW3D_PT_Print3D_Object, + ui.VIEW3D_PT_Print3D_Mesh, - operators.Print3DInfoVolume, - operators.Print3DInfoArea, + operators.MESH_OT_Print3D_Info_Volume, + operators.MESH_OT_Print3D_Info_Area, - operators.Print3DCheckDegenerate, - operators.Print3DCheckDistorted, - operators.Print3DCheckSolid, - operators.Print3DCheckIntersections, - operators.Print3DCheckThick, - operators.Print3DCheckSharp, - operators.Print3DCheckOverhang, - operators.Print3DCheckAll, + operators.MESH_OT_Print3D_Check_Degenerate, + operators.MESH_OT_Print3D_Check_Distorted, + operators.MESH_OT_Print3D_Check_Solid, + operators.MESH_OT_Print3D_Check_Intersections, + operators.MESH_OT_Print3D_Check_Thick, + operators.MESH_OT_Print3D_Check_Sharp, + operators.MESH_OT_Print3D_Check_Overhang, + operators.MESH_OT_Print3D_Check_All, - operators.Print3DCleanIsolated, - operators.Print3DCleanDistorted, - # operators.Print3DCleanThin, - operators.Print3DCleanNonManifold, + operators.MESH_OT_Print3D_Clean_Isolated, + operators.MESH_OT_Print3D_Clean_Distorted, + # operators.MESH_OT_Print3D_Clean_Thin, + operators.MESH_OT_Print3D_Clean_Non_Manifold, - operators.Print3DSelectReport, + operators.MESH_OT_Print3D_Select_Report, - operators.Print3DScaleToVolume, - operators.Print3DScaleToBounds, + operators.MESH_OT_Print3D_Scale_To_Volume, + operators.MESH_OT_Print3D_Scale_To_Bounds, - operators.Print3DExport, + operators.MESH_OT_Print3D_Export, - Print3DSettings, - printpreferences, + Print3D_Scene_Props, + Print3D_Preferences, ) @@ -201,9 +196,9 @@ def register(): for cls in classes: bpy.utils.register_class(cls) - bpy.types.Scene.print_3d = PointerProperty(type=Print3DSettings) + bpy.types.Scene.print_3d = PointerProperty(type=Print3D_Scene_Props) - update_panel(None, bpy.context) + update_panels(None, bpy.context) def unregister(): diff --git a/release/scripts/addons/object_print3d_utils/operators.py b/release/scripts/addons/object_print3d_utils/operators.py index 01ef62ccac13..8dcdf2110ea9 100644 --- a/release/scripts/addons/object_print3d_utils/operators.py +++ b/release/scripts/addons/object_print3d_utils/operators.py @@ -21,12 +21,12 @@ # All Operator import bpy -import bmesh from bpy.types import Operator from bpy.props import ( IntProperty, FloatProperty, ) +import bmesh from . import ( mesh_helpers, @@ -48,7 +48,7 @@ def clean_float(text): # Mesh Info -class Print3DInfoVolume(Operator): +class MESH_OT_Print3D_Info_Volume(Operator): """Report the volume of the active mesh""" bl_idname = "mesh.print3d_info_volume" bl_label = "Print3D Info Volume" @@ -75,7 +75,7 @@ def execute(self, context): return {'FINISHED'} -class Print3DInfoArea(Operator): +class MESH_OT_Print3D_Info_Area(Operator): """Report the surface area of the active mesh""" bl_idname = "mesh.print3d_info_area" bl_label = "Print3D Info Area" @@ -122,7 +122,7 @@ def multiple_obj_warning(self, context): self.report({"INFO"}, "Multiple selected objects. Only the active one will be evaluated") -class Print3DCheckSolid(Operator): +class MESH_OT_Print3D_Check_Solid(Operator): """Check for geometry is solid (has valid inside/outside) and correct normals""" bl_idname = "mesh.print3d_check_solid" bl_label = "Print3D Check Solid" @@ -150,7 +150,7 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckIntersections(Operator): +class MESH_OT_Print3D_Check_Intersections(Operator): """Check geometry for self intersections""" bl_idname = "mesh.print3d_check_intersect" bl_label = "Print3D Check Intersections" @@ -165,7 +165,7 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckDegenerate(Operator): +class MESH_OT_Print3D_Check_Degenerate(Operator): """Check for degenerate geometry that may not print properly """ \ """(zero area faces, zero length edges)""" bl_idname = "mesh.print3d_check_degenerate" @@ -195,7 +195,7 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckDistorted(Operator): +class MESH_OT_Print3D_Check_Distorted(Operator): """Check for non-flat faces """ bl_idname = "mesh.print3d_check_distort" bl_label = "Print3D Check Distorted Faces" @@ -233,7 +233,7 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckThick(Operator): +class MESH_OT_Print3D_Check_Thick(Operator): """Check geometry is above the minimum thickness preference """ \ """(relies on correct normals)""" bl_idname = "mesh.print3d_check_thick" @@ -253,7 +253,7 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckSharp(Operator): +class MESH_OT_Print3D_Check_Sharp(Operator): """Check edges are below the sharpness preference""" bl_idname = "mesh.print3d_check_sharp" bl_label = "Print3D Check Sharp" @@ -278,7 +278,7 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckOverhang(Operator): +class MESH_OT_Print3D_Check_Overhang(Operator): """Check faces don't overhang past a certain angle""" bl_idname = "mesh.print3d_check_overhang" bl_label = "Print3D Check Overhang" @@ -314,19 +314,19 @@ def execute(self, context): return execute_check(self, context) -class Print3DCheckAll(Operator): +class MESH_OT_Print3D_Check_All(Operator): """Run all checks""" bl_idname = "mesh.print3d_check_all" bl_label = "Print3D Check All" check_cls = ( - Print3DCheckSolid, - Print3DCheckIntersections, - Print3DCheckDegenerate, - Print3DCheckDistorted, - Print3DCheckThick, - Print3DCheckSharp, - Print3DCheckOverhang, + MESH_OT_Print3D_Check_Solid, + MESH_OT_Print3D_Check_Intersections, + MESH_OT_Print3D_Check_Degenerate, + MESH_OT_Print3D_Check_Distorted, + MESH_OT_Print3D_Check_Thick, + MESH_OT_Print3D_Check_Sharp, + MESH_OT_Print3D_Check_Overhang, ) def execute(self, context): @@ -343,7 +343,7 @@ def execute(self, context): return {'FINISHED'} -class Print3DCleanIsolated(Operator): +class MESH_OT_Print3D_Clean_Isolated(Operator): """Cleanup isolated vertices and edges""" bl_idname = "mesh.print3d_clean_isolated" bl_label = "Print3D Clean Isolated " @@ -407,7 +407,7 @@ def vert_is_isolated(ele): return {'CANCELLED'} -class Print3DCleanDistorted(Operator): +class MESH_OT_Print3D_Clean_Distorted(Operator): """Tessellate distorted faces""" bl_idname = "mesh.print3d_clean_distorted" bl_label = "Print3D Clean Distorted" @@ -440,7 +440,7 @@ def face_is_distorted(ele): return {'CANCELLED'} -class Print3DCleanNonManifold(Operator): +class MESH_OT_Print3D_Clean_Non_Manifold(Operator): """Cleanup problems, like holes, non-manifold vertices, and inverted normals""" bl_idname = "mesh.print3d_clean_non_manifold" bl_label = "Print3D Clean Non-Manifold and Inverted" @@ -594,7 +594,7 @@ def delete_newly_generated_non_manifold_verts(cls): bpy.ops.mesh.delete(type='VERT') -class Print3DCleanThin(Operator): +class MESH_OT_Print3D_Clean_Thin(Operator): """Ensure minimum thickness""" bl_idname = "mesh.print3d_clean_thin" bl_label = "Print3D Clean Thin" @@ -610,7 +610,7 @@ def execute(self, context): # Select Report # ... helper function for info UI -class Print3DSelectReport(Operator): +class MESH_OT_Print3D_Select_Report(Operator): """Select the data associated with this report""" bl_idname = "mesh.print3d_select_report" bl_label = "Print3D Select Report" @@ -641,7 +641,7 @@ def execute(self, context): bpy.ops.mesh.select_mode(type=self._type_to_mode[bm_type]) bm = bmesh.from_edit_mesh(obj.data) - elems = getattr(bm, Print3DSelectReport._type_to_attr[bm_type])[:] + elems = getattr(bm, MESH_OT_Print3D_Select_Report._type_to_attr[bm_type])[:] try: for i in bm_array: @@ -669,7 +669,7 @@ def _scale(scale, report=None, report_suffix=""): report({'INFO'}, "Scaled by %s%s" % (clean_float("%.6f" % scale), report_suffix)) -class Print3DScaleToVolume(Operator): +class MESH_OT_Print3D_Scale_To_Volume(Operator): """Scale edit-mesh or selected-objects to a set volume""" bl_idname = "mesh.print3d_scale_to_volume" bl_label = "Scale to Volume" @@ -715,7 +715,7 @@ def calc_volume(obj): return wm.invoke_props_dialog(self) -class Print3DScaleToBounds(Operator): +class MESH_OT_Print3D_Scale_To_Bounds(Operator): """Scale edit-mesh or selected-objects to fit within a maximum length""" bl_idname = "mesh.print3d_scale_to_bounds" bl_label = "Scale to Bounds" @@ -769,7 +769,7 @@ def calc_length(vecs): # ------ # Export -class Print3DExport(Operator): +class MESH_OT_Print3D_Export(Operator): """Export active object using print3d settings""" bl_idname = "mesh.print3d_export" bl_label = "Print3D Export" diff --git a/release/scripts/addons/object_print3d_utils/ui.py b/release/scripts/addons/object_print3d_utils/ui.py index dd286557b826..6e5c32849266 100644 --- a/release/scripts/addons/object_print3d_utils/ui.py +++ b/release/scripts/addons/object_print3d_utils/ui.py @@ -20,12 +20,13 @@ # Interface for this addon. -import bmesh from bpy.types import Panel +import bmesh + from . import report -class Print3DToolBar: +class Print3D_ToolBar: bl_label = "Print3D" bl_space_type = 'VIEW_3D' bl_region_type = 'TOOLS' @@ -39,7 +40,7 @@ class Print3DToolBar: @classmethod def poll(cls, context): obj = context.active_object - return obj and obj.type == 'MESH' and context.mode in {'OBJECT','EDIT_MESH'} + return obj and obj.type == 'MESH' @staticmethod def draw_report(layout, context): @@ -57,7 +58,7 @@ def draw_report(layout, context): bm_type, bm_array = data col.operator("mesh.print3d_select_report", text=text, - icon=Print3DToolBar._type_to_icon[bm_type]).index = i + icon=Print3D_ToolBar._type_to_icon[bm_type]).index = i layout.operator("mesh.select_non_manifold", text='Non Manifold Extended') else: col.label(text) @@ -130,17 +131,17 @@ def draw(self, context): rowsub.prop(print_3d, "export_format", text="") rowsub.operator("mesh.print3d_export", text="Export", icon='EXPORT') - Print3DToolBar.draw_report(layout, context) + Print3D_ToolBar.draw_report(layout, context) # So we can have a panel in both object mode and editmode -class Print3DToolBarObject(Panel, Print3DToolBar): +class VIEW3D_PT_Print3D_Object(Panel, Print3D_ToolBar): bl_category = "3D Printing" - bl_idname = "MESH_PT_print3d_object" + bl_idname = "VIEW3D_PT_print3d_object" bl_context = "objectmode" -class Print3DToolBarMesh(Panel, Print3DToolBar): +class VIEW3D_PT_Print3D_Mesh(Panel, Print3D_ToolBar): bl_category = "3D Printing" - bl_idname = "MESH_PT_print3d_mesh" + bl_idname = "VIEW3D_PT_print3d_mesh" bl_context = "mesh_edit" diff --git a/release/scripts/addons/oscurart_tools/__init__.py b/release/scripts/addons/oscurart_tools/__init__.py index 72a40a3d4d6d..8affc8bbf61e 100644 --- a/release/scripts/addons/oscurart_tools/__init__.py +++ b/release/scripts/addons/oscurart_tools/__init__.py @@ -178,6 +178,7 @@ def poll(cls, context): return context.scene.oscurart.osc_mesh_tools def draw(self, context): + scene = context.scene layout = self.layout col = layout.column(align=1) @@ -197,10 +198,18 @@ def draw(self, context): colrow = col.row(align=1) colrow.operator("mesh.overlap_uv_faces", icon="UV_FACESEL") colrow = col.row(align=1) - colrow.operator("view3d.modal_operator", icon="STICKY_UVS_DISABLE") + colrow.operator("mesh.uv_island_copy", icon="COPYDOWN") + colrow.operator("mesh.uv_island_paste", icon="PASTEDOWN") + colrow = col.row(align=1) + colrow.operator("view3d.modal_operator", icon="STICKY_UVS_DISABLE") colrow = col.row(align=1) colrow.operator("lattice.mirror_selected", icon="LATTICE_DATA") - + colrow = col.row(align=1) + colrow.label(text="Edit Multimesh") + colrow.prop_search(scene, "multimeshedit", bpy.data, "groups", text="") + colrow = col.row(align=1) + colrow.operator("mesh.create_edit_multimesh", icon="IMPORT", text= "StartEdit") + colrow.operator("mesh.apply_edit_multimesh", icon="EXPORT", text="FinishEdit") class OscPanelShapes(Panel): bl_idname = "Oscurart Shapes Tools" @@ -222,8 +231,8 @@ def draw(self, context): col.operator("mesh.create_lmr_groups_osc", icon="GROUP_VERTEX") col.operator("mesh.split_lr_shapes_osc", icon="SHAPEKEY_DATA") colrow = col.row(align=1) - colrow.operator("mesh.create_symmetrical_layout_osc", icon="SETTINGS") - colrow.operator("mesh.create_asymmetrical_layout_osc", icon="SETTINGS") + colrow.operator("mesh.create_symmetrical_layout_osc", icon="IMPORT") + colrow.operator("mesh.create_asymmetrical_layout_osc", icon="EXPORT") class OscPanelRender(Panel): @@ -433,6 +442,7 @@ def draw(self, context): def register(): + from bpy.types import Scene bpy.utils.register_module(__name__) bpy.types.Scene.oscurart = PointerProperty(type=View3DOscPanel) @@ -442,6 +452,8 @@ def register(): bpy.types.Scene.quick_animation_in = IntProperty(default=1) bpy.types.Scene.quick_animation_out = IntProperty(default=250) + Scene.multimeshedit = StringProperty() + # SETEO VARIABLE DE ENTORNO bpy.types.Scene.SearchAndSelectOt = StringProperty( default="Object name initials" diff --git a/release/scripts/addons/oscurart_tools/oscurart_meshes.py b/release/scripts/addons/oscurart_tools/oscurart_meshes.py index f6ba46ff9b47..162a116334c2 100644 --- a/release/scripts/addons/oscurart_tools/oscurart_meshes.py +++ b/release/scripts/addons/oscurart_tools/oscurart_meshes.py @@ -19,6 +19,7 @@ # import bpy +from mathutils import Vector from bpy.types import Operator from bpy.props import ( IntProperty, @@ -562,3 +563,155 @@ def poll(cls, context): def execute(self, context): defLatticeMirror(self, context) return {'FINISHED'} + + +# -------------------------- OVERLAP UV ISLANDS + +def defCopyUvsIsland(self, context): + bpy.ops.object.mode_set(mode="OBJECT") + global obLoop + global islandFaces + obLoop = [] + islandFaces = [] + for poly in bpy.context.object.data.polygons: + if poly.select: + islandFaces.append(poly.index) + for li in poly.loop_indices: + obLoop.append(li) + + bpy.ops.object.mode_set(mode="EDIT") + +def defPasteUvsIsland(self, uvOffset, rotateUv,context): + bpy.ops.object.mode_set(mode="OBJECT") + selPolys = [poly.index for poly in bpy.context.object.data.polygons if poly.select] + + for island in selPolys: + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.select_all(action="DESELECT") + bpy.ops.object.mode_set(mode="OBJECT") + bpy.context.object.data.polygons[island].select = True + bpy.ops.object.mode_set(mode="EDIT") + bpy.ops.mesh.select_linked() + bpy.ops.object.mode_set(mode="OBJECT") + TobLoop = [] + TislandFaces = [] + for poly in bpy.context.object.data.polygons: + if poly.select: + TislandFaces.append(poly.index) + for li in poly.loop_indices: + TobLoop.append(li) + + for source,target in zip(range(min(obLoop),max(obLoop)+1),range(min(TobLoop),max(TobLoop)+1)): + bpy.context.object.data.uv_layers.active.data[target].uv = bpy.context.object.data.uv_layers.active.data[source].uv + Vector((uvOffset,0)) + + bpy.ops.object.mode_set(mode="EDIT") + + if rotateUv: + bpy.ops.object.mode_set(mode="OBJECT") + for poly in selPolys: + bpy.context.object.data.polygons[poly].select = True + bpy.ops.object.mode_set(mode="EDIT") + bm = bmesh.from_edit_mesh(bpy.context.object.data) + bmesh.ops.reverse_uvs(bm, faces=[f for f in bm.faces if f.select]) + bmesh.ops.rotate_uvs(bm, faces=[f for f in bm.faces if f.select]) + #bmesh.update_edit_mesh(bpy.context.object.data, tessface=False, destructive=False) + + + +class CopyUvIsland(Operator): + """Copy Uv Island""" + bl_idname = "mesh.uv_island_copy" + bl_label = "Copy Uv Island" + bl_options = {"REGISTER", "UNDO"} + + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH' and + context.active_object.mode == "EDIT") + + def execute(self, context): + defCopyUvsIsland(self, context) + return {'FINISHED'} + +class PasteUvIsland(Operator): + """Paste Uv Island""" + bl_idname = "mesh.uv_island_paste" + bl_label = "Paste Uv Island" + bl_options = {"REGISTER", "UNDO"} + + uvOffset = BoolProperty( + name="Uv Offset", + default=False + ) + + rotateUv = BoolProperty( + name="Rotate Uv Corner", + default=False + ) + @classmethod + def poll(cls, context): + return (context.active_object is not None and + context.active_object.type == 'MESH' and + context.active_object.mode == "EDIT") + + def execute(self, context): + defPasteUvsIsland(self, self.uvOffset, self.rotateUv, context) + return {'FINISHED'} + + + +class createEditMultimesh(Operator): + """Create Edit Multi Mesh""" + bl_idname = "mesh.create_edit_multimesh" + bl_label = "Create edit multimesh" + bl_options = {"REGISTER", "UNDO"} + + + # creo el merge para editar + def execute(self,context): + global relvert + global me + global ob + temp = [[ob , [vert.co for vert in ob.data.vertices]]for ob in bpy.data.groups[bpy.context.scene.multimeshedit].objects] + vi = 0 + pi = 0 + relvert = {} + vertlist = [] + polylist = [] + for ob in temp: + objectMatrix = ob[0].matrix_world.copy() + for vert in ob[0].data.vertices: + vertlist.append(objectMatrix*vert.co) + for poly in ob[0].data.polygons: + polylist.append(tuple([vert+vi for vert in poly.vertices[:]])) + relvert[ob[0]] = {vert.index:vert.index+vi for vert in ob[0].data.vertices} + vi += len(ob[0].data.vertices) + ob[0].hide = 1 + me = bpy.data.meshes.new("editMesh") + ob = bpy.data.objects.new("editMesh", me) + bpy.context.scene.objects.link(ob) + me.from_pydata(vertlist,[],polylist) + bpy.ops.object.select_all(action="DESELECT") + bpy.context.scene.objects.active = ob + bpy.ops.object.mode_set(mode='EDIT', toggle=False) + return {'FINISHED'} + + +class ApplyEditMultimesh(Operator): + """Apply Edit Multi Mesh""" + bl_idname = "mesh.apply_edit_multimesh" + bl_label = "Apply edit multimesh" + bl_options = {"REGISTER", "UNDO"} + + def execute(self,context): + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + for object,rv in relvert.items(): + objectMatrix = object.matrix_world.inverted().copy() + for source, target in rv.items(): + object.data.vertices[source].co = objectMatrix * me.vertices[target].co + object.hide = 0 + bpy.context.scene.objects.unlink(ob) + return {'FINISHED'} + + \ No newline at end of file diff --git a/release/scripts/addons/paint_palette.py b/release/scripts/addons/paint_palette.py index 7597fde1af07..577b292bf08f 100644 --- a/release/scripts/addons/paint_palette.py +++ b/release/scripts/addons/paint_palette.py @@ -22,7 +22,7 @@ bl_info = { "name": "Paint Palettes", "author": "Dany Lebel (Axon D)", - "version": (0, 9, 3), + "version": (0, 9, 4), "blender": (2, 63, 0), "location": "Image Editor and 3D View > Any Paint mode > Color Palette or Weight Palette panel", "description": "Palettes for color and weight paint modes", @@ -44,20 +44,20 @@ import bpy from bpy.types import ( - Operator, - Menu, - Panel, - PropertyGroup, - ) + Operator, + Menu, + Panel, + PropertyGroup, +) from bpy.props import ( - BoolProperty, - FloatProperty, - FloatVectorProperty, - IntProperty, - StringProperty, - PointerProperty, - CollectionProperty, - ) + BoolProperty, + FloatProperty, + FloatVectorProperty, + IntProperty, + StringProperty, + PointerProperty, + CollectionProperty, +) def update_panels(): @@ -97,6 +97,14 @@ def update_weight_value(): return None +def check_path_return(): + from os.path import normpath + preset_path = bpy.path.abspath(bpy.context.scene.palette_props.presets_folder) + paths = normpath(preset_path) + + return paths if paths else "" + + class PALETTE_MT_menu(Menu): bl_label = "Presets" preset_subdir = "" @@ -109,61 +117,62 @@ def path_menu(self, searchpaths, operator, props_default={}): import bpy.utils layout = self.layout + + if bpy.data.filepath == "": + layout.label("*Please save the .blend file first*") + return + if not searchpaths[0]: layout.label("* Missing Paths *") + return # collect paths - else: - files = [] - for directory in searchpaths: - files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)]) + files = [] + for directory in searchpaths: + files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)]) - files.sort() + files.sort() - for f, filepath in files: + for f, filepath in files: - if f.startswith("."): - continue - # do not load everything from the given folder, only .gpl files - if f[-4:] != ".gpl": - continue + if f.startswith("."): + continue + # do not load everything from the given folder, only .gpl files + if f[-4:] != ".gpl": + continue - preset_name = bpy.path.display_name(f) - props = layout.operator(operator, text=preset_name) + preset_name = bpy.path.display_name(f) + props = layout.operator(operator, text=preset_name) - for attr, value in props_default.items(): - setattr(props, attr, value) + for attr, value in props_default.items(): + setattr(props, attr, value) - props.filepath = filepath - if operator == "palette.load_gimp_palette": - props.menu_idname = self.bl_idname + props.filepath = filepath + if operator == "palette.load_gimp_palette": + props.menu_idname = self.bl_idname def draw_preset(self, context): - """Define these on the subclass - - preset_operator - - preset_subdir - """ - import bpy - self.path_menu([bpy.context.scene.palette_props.presets_folder], self.preset_operator) + paths = check_path_return() + self.path_menu([paths], self.preset_operator) draw = draw_preset -class LoadGimpPalette(Operator): +class PALETTE_OT_load_gimp_palette(Operator): """Execute a preset""" bl_idname = "palette.load_gimp_palette" bl_label = "Load a Gimp palette" filepath = StringProperty( - name="Path", - description="Path of the .gpl file to load", - default="" - ) + name="Path", + description="Path of the .gpl file to load", + default="" + ) menu_idname = StringProperty( - name="Menu ID Name", - description="ID name of the menu this was called from", - default="" - ) + name="Menu ID Name", + description="ID name of the menu this was called from", + default="" + ) def execute(self, context): from os.path import basename @@ -255,14 +264,16 @@ class WriteGimpPalette(): bl_options = {'REGISTER'} # only because invoke_props_popup requires name = StringProperty( - name="Name", - description="Name of the preset, used to make the path name", - maxlen=64, default="" - ) + name="Name", + description="Name of the preset, used to make the path name", + maxlen=64, + options={'SKIP_SAVE'}, + default="" + ) remove_active = BoolProperty( - default=False, - options={'HIDDEN'} - ) + default=False, + options={'HIDDEN'} + ) @staticmethod def as_filename(name): # could reuse for other presets @@ -278,26 +289,25 @@ def execute(self, context): self.pre_cb(context) preset_menu_class = getattr(bpy.types, self.preset_menu) + target_path = check_path_return() - if not self.remove_active: + if not target_path: + self.report({'WARNING'}, "Failed to create presets path") + return {'CANCELLED'} + + if not os.path.exists(target_path): + self.report({'WARNING'}, + "Failure to open the saved Palettes Folder. Check if the path exists") + return {'CANCELLED'} + if not self.remove_active: if not self.name: + self.report({'INFO'}, + "No name is given for the preset entry. Operation Cancelled") return {'FINISHED'} filename = self.as_filename(self.name) - target_path = pp.presets_folder - - if not target_path: - self.report({'WARNING'}, "Failed to create presets path") - return {'CANCELLED'} - - if not os.path.exists(target_path): - self.report({'WARNING'}, - "Failure to open the saved Palletes Folder. Check if the path exists") - return {'CANCELLED'} - filepath = os.path.join(target_path, filename) + ".gpl" - file_preset = open(filepath, 'wb') gpl = "GIMP Palette\n" gpl += "Name: %s\n" % filename @@ -312,18 +322,19 @@ def execute(self, context): file_preset.close() pp.palette_name = filename + preset_menu_class.bl_label = bpy.path.display_name(filename) + + self.report({'INFO'}, "Created Palette: {}".format(filepath)) else: preset_active = preset_menu_class.bl_label + filename = self.as_filename(preset_active) - # fairly sloppy but convenient. - filepath = bpy.utils.preset_find(preset_active, self.preset_subdir) - - if not filepath: - filepath = bpy.utils.preset_find(preset_active, - self.preset_subdir, display_name=True) + filepath = os.path.join(target_path, filename) + ".gpl" - if not filepath: + if not filepath or not os.path.exists(filepath): + self.report({'WARNING'}, "Preset could not be found. Operation Cancelled") + self.reset_preset_name(preset_menu_class, pp) return {'CANCELLED'} if hasattr(self, "remove"): @@ -331,18 +342,24 @@ def execute(self, context): else: try: os.remove(filepath) + self.report({'INFO'}, "Deleted palette: {}".format(filepath)) except: import traceback traceback.print_exc() - # XXX, stupid! - preset_menu_class.bl_label = "Presets" + self.reset_preset_name(preset_menu_class, pp) if hasattr(self, "post_cb"): self.post_cb(context) return {'FINISHED'} + @staticmethod + def reset_preset_name(presets, props): + # XXX, still stupid! + presets.bl_label = "Presets" + props.palette_name = "" + def check(self, context): self.name = self.as_filename(self.name) @@ -350,11 +367,11 @@ def invoke(self, context, event): if not self.remove_active: wm = context.window_manager return wm.invoke_props_dialog(self) - else: - return self.execute(context) + return self.execute(context) -class AddPresetPalette(WriteGimpPalette, Operator): + +class PALETTE_OT_preset_add(WriteGimpPalette, Operator): bl_idname = "palette.preset_add" bl_label = "Add Palette Preset" preset_menu = "PALETTE_MT_menu" @@ -439,14 +456,13 @@ def invoke(self, context, event): def color_palette_draw(self, context): - palette_props = bpy.context.scene.palette_props + palette_props = context.scene.palette_props layout = self.layout - bpy.types.PALETTE_MT_menu.preset_subdir = palette_props.presets_folder row = layout.row(align=True) - row.menu("PALETTE_MT_menu", text=palette_props.palette_name.rstrip()) - row.operator("palette.preset_add", text="", icon="ZOOMIN") + row.menu("PALETTE_MT_menu", text=PALETTE_MT_menu.bl_label) + row.operator("palette.preset_add", text="", icon="ZOOMIN").remove_active = False row.operator("palette.preset_add", text="", icon="ZOOMOUT").remove_active = True col = layout.column(align=True) @@ -487,8 +503,6 @@ def color_palette_draw(self, context): row = layout.row() row.prop(palette_props, "presets_folder", text="") - pass - class BrushButtonsPanel(): bl_space_type = 'IMAGE_EDITOR' @@ -612,11 +626,12 @@ class VIEW3D_OT_reset_weight_palette(Operator): def execute(self, context): try: palette_props = context.scene.palette_props - dict_defs = {0: 0.0, 1: 0.1, 2: 0.25, - 3: 0.333, 4: 0.4, 5: 0.5, - 6: 0.6, 7: 0.6666, 8: 0.75, - 9: 0.9, 10: 1.0 - } + dict_defs = { + 0: 0.0, 1: 0.1, 2: 0.25, + 3: 0.333, 4: 0.4, 5: 0.5, + 6: 0.6, 7: 0.6666, 8: 0.75, + 9: 0.9, 10: 1.0 + } current_idx = palette_props.current_weight_index palette_props.weight = dict_defs[current_idx] @@ -673,30 +688,20 @@ def draw(self, context): row.operator("paint.reset_weight_palette", text="Reset") -class Colors(PropertyGroup): +class PALETTE_Colors(PropertyGroup): """Class for colors CollectionProperty""" color = FloatVectorProperty( - name="", - description="", - default=(0.8, 0.8, 0.8), - min=0, max=1, - step=1, precision=3, - subtype='COLOR_GAMMA', - size=3 - ) - - -class Weights(PropertyGroup): - """Class for Weights Collection Property""" - weight = FloatProperty( - default=0.0, - min=0.0, - max=1.0, - precision=3 - ) + name="", + description="", + default=(0.8, 0.8, 0.8), + min=0, max=1, + step=1, precision=3, + subtype='COLOR_GAMMA', + size=3 + ) -class PaletteProps(PropertyGroup): +class PALETTE_Props(PropertyGroup): def update_color_name(self, context): pp = bpy.context.scene.palette_props @@ -742,133 +747,151 @@ def update_weight(self, context): return None palette_name = StringProperty( - name="Palette Name", - default="Preset", - subtype='FILE_NAME' - ) + name="Palette Name", + default="Preset", + subtype='FILE_NAME' + ) color_name = StringProperty( - name="", - description="Color Name", - default="Untitled", - update=update_color_name - ) + name="", + description="Color Name", + default="Untitled", + update=update_color_name + ) columns = IntProperty( - name="Columns", - description="Number of Columns", - min=0, max=16, - default=0 - ) + name="Columns", + description="Number of Columns", + min=0, max=16, + default=0 + ) index = IntProperty( - name="Index", - description="Move Selected Color", - min=0, - update=move_color - ) + name="Index", + description="Move Selected Color", + min=0, + update=move_color + ) notes = StringProperty( - name="Palette Notes", - default="#\n" - ) + name="Palette Notes", + default="#\n" + ) current_color_index = IntProperty( - name="Current Color Index", - description="", - default=0, - min=0 - ) + name="Current Color Index", + description="", + default=0, + min=0 + ) current_weight_index = IntProperty( - name="Current Color Index", - description="", - default=10, - min=-1 - ) + name="Current Color Index", + description="", + default=10, + min=-1 + ) presets_folder = StringProperty(name="", - description="Palettes Folder", - subtype="DIR_PATH" - ) + description="Palettes Folder", + subtype="DIR_PATH", + default="//" + ) colors = CollectionProperty( - type=Colors - ) + type=PALETTE_Colors + ) weight = FloatProperty( - name="Weight", - description="Modify the active Weight preset slot value", - default=0.0, - min=0.0, max=1.0, - precision=3, - update=update_weight - ) + name="Weight", + description="Modify the active Weight preset slot value", + default=0.0, + min=0.0, max=1.0, + precision=3, + update=update_weight + ) weight_0 = FloatProperty( - default=0.0, - min=0.0, max=1.0, - precision=3 - ) + default=0.0, + min=0.0, max=1.0, + precision=3 + ) weight_1 = FloatProperty( - default=0.1, - min=0.0, max=1.0, - precision=3 - ) + default=0.1, + min=0.0, max=1.0, + precision=3 + ) weight_2 = FloatProperty( - default=0.25, - min=0.0, max=1.0, - precision=3 - ) + default=0.25, + min=0.0, max=1.0, + precision=3 + ) weight_3 = FloatProperty( - default=0.333, - min=0.0, max=1.0, - precision=3 - ) + default=0.333, + min=0.0, max=1.0, + precision=3 + ) weight_4 = FloatProperty( - default=0.4, - min=0.0, max=1.0, - precision=3 - ) + default=0.4, + min=0.0, max=1.0, + precision=3 + ) weight_5 = FloatProperty( - default=0.5, - min=0.0, max=1.0, - precision=3 - ) + default=0.5, + min=0.0, max=1.0, + precision=3 + ) weight_6 = FloatProperty( - default=0.6, - min=0.0, max=1.0, - precision=3 - ) + default=0.6, + min=0.0, max=1.0, + precision=3 + ) weight_7 = FloatProperty( - default=0.6666, - min=0.0, max=1.0, - precision=3 - ) + default=0.6666, + min=0.0, max=1.0, + precision=3 + ) weight_8 = FloatProperty( - default=0.75, - min=0.0, max=1.0, - precision=3 - ) + default=0.75, + min=0.0, max=1.0, + precision=3 + ) weight_9 = FloatProperty( - default=0.9, - min=0.0, max=1.0, - precision=3 - ) + default=0.9, + min=0.0, max=1.0, + precision=3 + ) weight_10 = FloatProperty( - default=1.0, - min=0.0, max=1.0, - precision=3 - ) - pass + default=1.0, + min=0.0, max=1.0, + precision=3 + ) + + +classes = ( + PALETTE_MT_menu, + PALETTE_OT_load_gimp_palette, + PALETTE_OT_preset_add, + PALETTE_OT_add_color, + PALETTE_OT_remove_color, + PALETTE_OT_sample_tool_color, + IMAGE_OT_select_color, + IMAGE_PT_color_palette, + VIEW3D_PT_color_palette, + VIEW3D_OT_select_weight, + VIEW3D_OT_reset_weight_palette, + VIEW3D_PT_weight_palette, + PALETTE_Colors, + PALETTE_Props, +) def register(): - bpy.utils.register_module(__name__) + for cls in classes: + bpy.utils.register_class(cls) bpy.types.Scene.palette_props = PointerProperty( - type=PaletteProps, - name="Palette Props", - description="" - ) - pass + type=PALETTE_Props, + name="Palette Props", + description="" + ) def unregister(): - bpy.utils.unregister_module(__name__) + for cls in reversed(classes): + bpy.utils.unregister_class(cls) del bpy.types.Scene.palette_props - pass if __name__ == "__main__": diff --git a/release/scripts/addons/render_povray/__init__.py b/release/scripts/addons/render_povray/__init__.py index cfb45f5a9c06..90d80f13ea2e 100644 --- a/release/scripts/addons/render_povray/__init__.py +++ b/release/scripts/addons/render_povray/__init__.py @@ -19,14 +19,13 @@ # bl_info = { - "name": "POVRAY-3.7", + "name": "POV-3.7", "author": "Campbell Barton, Silvio Falcinelli, Maurice Raybaud, " "Constantin Rahn, Bastien Montagne, Leonid Desyatkov", - "version": (0, 0, 9), - "blender": (2, 75, 0), + "version": (0, 1, 0), + "blender": (2, 79, 0), "location": "Render > Engine > POV-Ray 3.7", "description": "Basic POV-Ray 3.7 integration for blender", - "warning": "this script is RC", "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/Render/POV-Ray", "category": "Render", @@ -44,6 +43,7 @@ #import addon_utils # To use some other addons import nodeitems_utils #for Nodes from nodeitems_utils import NodeCategory, NodeItem #for Nodes + from bl_operators.presets import AddPresetBase from bpy.types import ( AddonPreferences, PropertyGroup, @@ -73,6 +73,11 @@ def string_strip_hyphen(name): # Scene POV properties. ############################################################################### class RenderPovSettingsScene(PropertyGroup): + #Linux SDL-window enable + sdl_window_enable = BoolProperty( + name="Enable SDL window", + description="Enable the SDL window in Linux OS", + default=True) # File Options text_block = StringProperty( name="Text Scene Name", @@ -121,7 +126,7 @@ class RenderPovSettingsScene(PropertyGroup): radio_enable = BoolProperty( name="Enable Radiosity", description="Enable POV-Rays radiosity calculation", - default=False) + default=True) radio_display_advanced = BoolProperty( name="Advanced Options", @@ -132,19 +137,88 @@ class RenderPovSettingsScene(PropertyGroup): name="Enable Media", description="Enable POV-Rays atmospheric media", default=False) + media_samples = IntProperty( name="Samples", description="Number of samples taken from camera to first object " "encountered along ray path for media calculation", min=1, max=100, default=35) - - media_color = FloatVectorProperty( - name="Media Color", description="The atmospheric media color", + + media_scattering_type = EnumProperty( + name="Scattering Type", + description="Scattering model", + items=(('1', "1 Isotropic", "The simplest form of scattering because" + " it is independent of direction."), + ('2', "2 Mie haze ", "For relatively small particles such as " + "minuscule water droplets of fog, cloud " + "particles, and particles responsible " + "for the polluted sky. In this model the" + " scattering is extremely directional in" + " the forward direction i.e. the amount " + "of scattered light is largest when the " + "incident light is anti-parallel to the " + "viewing direction (the light goes " + "directly to the viewer). It is smallest" + " when the incident light is parallel to" + " the viewing direction. "), + ('3', "3 Mie murky", "Like haze but much more directional"), + ('4', "4 Rayleigh", "For extremely small particles such as " + "molecules of the air. The amount of " + "scattered light depends on the incident" + " light angle. It is largest when the " + "incident light is parallel or " + "anti-parallel to the viewing direction " + "and smallest when the incident light is " + "perpendicular to viewing direction."), + ('5', "5 Henyey-Greenstein", "The default eccentricity value " + "of zero defines isotropic " + "scattering while positive " + "values lead to scattering in " + "the direction of the light and " + "negative values lead to " + "scattering in the opposite " + "direction of the light. Larger " + "values of e (or smaller values " + "in the negative case) increase " + "the directional property of the" + " scattering.")), + default='1') + + media_diffusion_scale = FloatProperty( + name="Scale", description="Scale factor of Media Diffusion Color", + precision=12, step=0.00000001, min=0.000000001, max=1.0, + default=(1.0)) + + media_diffusion_color = FloatVectorProperty( + name="Media Diffusion Color", description="The atmospheric media color", precision=4, step=0.01, min=0, soft_max=1, default=(0.001, 0.001, 0.001), options={'ANIMATABLE'}, subtype='COLOR') + media_absorption_scale = FloatProperty( + name="Scale", description="Scale factor of Media Absorption Color. " + "use 1/depth of media volume in meters", + precision=12, step=0.000001, min=0.000000001, max=1.0, + default=(0.00002)) + + media_absorption_color = FloatVectorProperty( + name="Media Absorption Color", description="The atmospheric media absorption color", + precision=4, step=0.01, min=0, soft_max=1, + default=(0.0, 0.0, 0.0), + options={'ANIMATABLE'}, + subtype='COLOR') + + media_eccentricity = FloatProperty( + name="Media Eccenticity Factor", description="Positive values lead" + " to scattering in the direction of the light and negative " + "values lead to scattering in the opposite direction of the " + "light. Larger values of e (or smaller values in the negative" + " case) increase the directional property of the scattering.", + precision=2, step=0.01, min=-1.0, max=1.0, + default=(0.0), + options={'ANIMATABLE'}) + baking_enable = BoolProperty( name="Enable Baking", description="Enable POV-Rays texture baking", @@ -377,7 +451,7 @@ class RenderPovSettingsScene(PropertyGroup): description="", maxlen=1024, subtype="FILE_PATH") - + #########RADIOSITY######## radio_adc_bailout = FloatProperty( name="ADC Bailout", description="The adc_bailout for radiosity rays. Use " @@ -422,7 +496,7 @@ class RenderPovSettingsScene(PropertyGroup): radio_media = BoolProperty( name="Media", description="Radiosity estimation can be affected by media", - default=False) + default=True) radio_subsurface = BoolProperty( name="Subsurface", description="Radiosity estimation can be affected by Subsurface Light Transport", @@ -468,7 +542,6 @@ class RenderPovSettingsScene(PropertyGroup): "in the mosaic preview last pass", min=0.000925, max=1.00, soft_min=0.01, soft_max=1.00, default=0.04, precision=3) - ############################################################################### # Material POV properties. ############################################################################### @@ -2199,20 +2272,14 @@ def draw(self, context): - - - - - - - - - def register(): bpy.utils.register_module(__name__) bpy.types.INFO_MT_add.prepend(ui.menu_func_add) bpy.types.INFO_MT_file_import.append(ui.menu_func_import) bpy.types.TEXT_MT_templates.append(ui.menu_func_templates) + bpy.types.RENDER_PT_povray_radiosity.prepend(ui.rad_panel_func) + bpy.types.LAMP_PT_POV_lamp.prepend(ui.lamp_panel_func) + bpy.types.WORLD_PT_world.prepend(ui.world_panel_func) # was used for parametric objects but made the other addon unreachable on # unregister for other tools to use created a user action call instead #addon_utils.enable("add_mesh_extra_objects", default_set=False, persistent=True) @@ -2244,6 +2311,9 @@ def unregister(): #bpy.types.TEXTURE_PT_context_texture.remove(TEXTURE_PT_povray_type) #addon_utils.disable("add_mesh_extra_objects", default_set=False) + bpy.types.WORLD_PT_world.remove(ui.world_panel_func) + bpy.types.LAMP_PT_POV_lamp.remove(ui.lamp_panel_func) + bpy.types.RENDER_PT_povray_radiosity.remove(ui.rad_panel_func) bpy.types.TEXT_MT_templates.remove(ui.menu_func_templates) bpy.types.INFO_MT_file_import.remove(ui.menu_func_import) bpy.types.INFO_MT_add.remove(ui.menu_func_add) diff --git a/release/scripts/addons/render_povray/render.py b/release/scripts/addons/render_povray/render.py index d36957807d07..9838f25db509 100644 --- a/release/scripts/addons/render_povray/render.py +++ b/release/scripts/addons/render_povray/render.py @@ -186,13 +186,17 @@ def safety(name, Level): ##############end safety string name material ##############################EndSF########################### +csg_list = [] + def is_renderable(scene, ob): - return (ob.is_visible(scene) and not ob.hide_render) + return (ob.is_visible(scene) and not ob.hide_render and ob not in csg_list) def renderable_objects(scene): return [ob for ob in bpy.data.objects if is_renderable(scene, ob)] +def no_renderable_objects(scene): + return [ob for ob in csg_list] tabLevel = 0 unpacked_images=[] @@ -628,7 +632,7 @@ def exportLamps(lamps): tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0)) # Area lights have no falloff type, so always use blenders lamp quad equivalent # for those? - tabWrite("fade_power %d\n" % 0) + tabWrite("fade_power %d\n" % 2) size_x = lamp.size samples_x = lamp.shadow_ray_samples_x if lamp.shape == 'SQUARE': @@ -1446,7 +1450,7 @@ def exportCurves(scene, ob): file.write(' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n') file.write(' #end\n') file.write('#end\n\n') - # Empty curves + # Empty curves if len(ob.data.splines)==0: tabWrite("\n//dummy sphere to represent empty curve location\n") tabWrite("#declare %s =\n"%dataname) @@ -1793,7 +1797,7 @@ def exportMeta(metas): # objectNames = {} DEF_OBJ_NAME = "Default" - def exportMeshes(scene, sel): + def exportMeshes(scene, sel, csg): # obmatslist = [] # def hasUniqueMaterial(): # # Grab materials attached to object instances ... @@ -2416,7 +2420,7 @@ def exportSmoke(smoke_obj_name): tabWrite("#declare %s = plane{ <0,0,1>,1\n"%povdataname) povMatName = "Default_texture" if ob.active_material: - #povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) + #povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name)) try: material = ob.active_material writeObjectMaterial(material, ob) @@ -3009,7 +3013,7 @@ def exportSmoke(smoke_obj_name): # POV object modifiers such as # hollow / sturm / double_illuminate etc. write_object_modifiers(scene,ob,file) - + #Importance for radiosity sampling added here: tabWrite("radiosity { \n") tabWrite("importance %3g \n" % importance) @@ -3254,7 +3258,7 @@ def exportSmoke(smoke_obj_name): ob.pov.inside_vector[1], ob.pov.inside_vector[2])) onceCSG = 1 - + if me.materials: try: material = me.materials[0] # dodgy @@ -3277,38 +3281,69 @@ def exportSmoke(smoke_obj_name): bpy.data.meshes.remove(me) - duplidata_ref = [] - for ob in sel: - #matrix = global_matrix * ob.matrix_world - if ob.is_duplicator: - tabWrite("\n//--DupliObjects in %s--\n\n"% ob.name) - ob.dupli_list_create(scene) - dup = "" - if ob.is_modified(scene, 'RENDER'): - #modified object always unique so using object name rather than data name - dup = "#declare OB%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name))) + if csg: + duplidata_ref = [] + for ob in sel: + #matrix = global_matrix * ob.matrix_world + if ob.is_duplicator: + tabWrite("\n//--DupliObjects in %s--\n\n"% ob.name) + ob.dupli_list_create(scene) + dup = "" + if ob.is_modified(scene, 'RENDER'): + #modified object always unique so using object name rather than data name + dup = "#declare OB%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name))) + else: + dup = "#declare DATA%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name))) + for eachduplicate in ob.dupli_list: + duplidataname = "OB"+string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name)) + dup += ("\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" %(string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name)), MatrixAsPovString(ob.matrix_world.inverted() * eachduplicate.matrix))) + #add object to a list so that it is not rendered for some dupli_types + if ob.dupli_type not in {'GROUP'} and duplidataname not in duplidata_ref: + duplidata_ref.append(duplidataname) #older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))] + dup += "}\n" + ob.dupli_list_clear() + tabWrite(dup) else: - dup = "#declare DATA%s = union{\n" %(string_strip_hyphen(bpy.path.clean_name(ob.name))) - for eachduplicate in ob.dupli_list: - duplidataname = "OB"+string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name)) - dup += ("\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" %(string_strip_hyphen(bpy.path.clean_name(bpy.data.objects[eachduplicate.object.name].data.name)), MatrixAsPovString(ob.matrix_world.inverted() * eachduplicate.matrix))) - #add object to a list so that it is not rendered for some dupli_types - if ob.dupli_type not in {'GROUP'} and duplidataname not in duplidata_ref: - duplidata_ref.append(duplidataname) #older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))] - dup += "}\n" - ob.dupli_list_clear() - tabWrite(dup) - else: - continue - print(duplidata_ref) - for data_name, inst in data_ref.items(): - for ob_name, matrix_str in inst: - if ob_name not in duplidata_ref: #.items() for a dictionary - tabWrite("\n//----Blender Object Name:%s----\n" % ob_name) - tabWrite("object { \n") - tabWrite("%s\n" % data_name) - tabWrite("%s\n" % matrix_str) - tabWrite("}\n") + continue + print(duplidata_ref) + for data_name, inst in data_ref.items(): + for ob_name, matrix_str in inst: + if ob_name not in duplidata_ref: #.items() for a dictionary + tabWrite("\n//----Blender Object Name:%s----\n" % ob_name) + if ob.pov.object_as == '': + tabWrite("object { \n") + tabWrite("%s\n" % data_name) + tabWrite("%s\n" % matrix_str) + tabWrite("}\n") + else: + no_boolean = True + for mod in ob.modifiers: + if mod.type == 'BOOLEAN': + operation = None + no_boolean = False + if mod.operation == 'INTERSECT': + operation = 'intersection' + else: + operation = mod.operation.lower() + mod_ob_name = string_strip_hyphen(bpy.path.clean_name(mod.object.name)) + mod_matrix = global_matrix * mod.object.matrix_world + mod_ob_matrix = MatrixAsPovString(mod_matrix) + tabWrite("%s { \n"%operation) + tabWrite("object { \n") + tabWrite("%s\n" % data_name) + tabWrite("%s\n" % matrix_str) + tabWrite("}\n") + tabWrite("object { \n") + tabWrite("%s\n" % ('DATA'+ mod_ob_name)) + tabWrite("%s\n" % mod_ob_matrix) + tabWrite("}\n") + tabWrite("}\n") + break + if no_boolean: + tabWrite("object { \n") + tabWrite("%s\n" % data_name) + tabWrite("%s\n" % matrix_str) + tabWrite("}\n") def exportWorld(world): render = scene.render @@ -3444,18 +3479,33 @@ def exportWorld(world): if mist.use_mist: tabWrite("fog {\n") - tabWrite("distance %.6f\n" % mist.depth) + if mist.falloff=='LINEAR': + tabWrite("distance %.6f\n" % ((mist.start+mist.depth)*0.368)) + elif mist.falloff=='QUADRATIC': # n**2 or squrt(n)? + tabWrite("distance %.6f\n" % ((mist.start+mist.depth)**2*0.368)) + elif mist.falloff=='INVERSE_QUADRATIC': # n**2 or squrt(n)? + tabWrite("distance %.6f\n" % ((mist.start+mist.depth)**2*0.368)) tabWrite("color rgbt<%.3g, %.3g, %.3g, %.3g>\n" % \ (*world.horizon_color, 1.0 - mist.intensity)) - #tabWrite("fog_offset %.6f\n" % mist.start) - #tabWrite("fog_alt 5\n") + #tabWrite("fog_offset %.6f\n" % mist.start) #create a pov property to prepend + #tabWrite("fog_alt %.6f\n" % mist.height) #XXX right? #tabWrite("turbulence 0.2\n") #tabWrite("turb_depth 0.3\n") - tabWrite("fog_type 1\n") + tabWrite("fog_type 1\n") #type2 for height tabWrite("}\n") if scene.pov.media_enable: tabWrite("media {\n") - tabWrite("scattering { 1, rgb <%.4g, %.4g, %.4g>}\n" % scene.pov.media_color[:]) + tabWrite("scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n" % \ + (int(scene.pov.media_scattering_type), + (scene.pov.media_diffusion_scale), + *(scene.pov.media_diffusion_color[:]))) + if scene.pov.media_scattering_type == '5': + tabWrite("eccentricity %.3g\n" % scene.pov.media_eccentricity) + tabWrite("}\n") + tabWrite("absorption %.12f*<%.4g, %.4g, %.4g>\n" % \ + (scene.pov.media_absorption_scale, + *(scene.pov.media_absorption_color[:]))) + tabWrite("\n") tabWrite("samples %.d\n" % scene.pov.media_samples) tabWrite("}\n") @@ -3559,7 +3609,7 @@ def exportCustomCode(): file.write(txt.as_string()) file.write("\n") - sel = renderable_objects(scene) + #sel = renderable_objects(scene) #removed for booleans if comments: file.write("//----------------------------------------------\n" \ "//--Exported with POV-Ray exporter for Blender--\n" \ @@ -3602,6 +3652,20 @@ def exportCustomCode(): if comments: file.write("\n//--Lamps--\n\n") + for ob in bpy.data.objects: + if ob.type == 'MESH': + for mod in ob.modifiers: + if mod.type == 'BOOLEAN': + if mod.object not in csg_list: + csg_list.append(mod.object) + if csg_list != []: + csg = False + sel = no_renderable_objects(scene) + exportMeshes(scene, sel, csg) + + csg = True + sel = renderable_objects(scene) + exportLamps([L for L in sel if (L.type == 'LAMP' and L.pov.object_as != 'RAINBOW')]) if comments: @@ -3655,7 +3719,7 @@ def exportCustomCode(): if comments: file.write("//--Mesh objects--\n") - exportMeshes(scene, sel) + exportMeshes(scene, sel, csg) #What follow used to happen here: #exportCamera() @@ -3745,7 +3809,7 @@ def write_pov_ini(scene, filename_ini, filename_log, filename_pov, filename_imag class PovrayRender(bpy.types.RenderEngine): bl_idname = 'POVRAY_RENDER' - bl_label = "POV-Ray 3.7" + bl_label = "Persitence Of Vision" DELAY = 0.5 @staticmethod @@ -3951,8 +4015,8 @@ def _test_wait(): self._temp_file_in = os.path.join(preview_dir, povPath) self._temp_file_ini = os.path.join(preview_dir, (os.path.splitext(self._temp_file_in)[0]+".INI")) self._temp_file_log = os.path.join(preview_dir, "alltext.out") - - + + ''' try: os.remove(self._temp_file_in) # so as not to load the old file @@ -4000,7 +4064,14 @@ def _test_wait(): # Start Rendering! try: - _process = subprocess.Popen([pov_binary, self._temp_file_ini] + extra_args, + if sys.platform[:3] != "win" and scene.pov.sdl_window_enable: #segfault on linux == False !!! + env = {'POV_DISPLAY_SCALED': 'off'} + env.update(os.environ) + self._process = subprocess.Popen([pov_binary, self._temp_file_ini], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + env=env) + else: + self._process = subprocess.Popen([pov_binary, self._temp_file_ini] + extra_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except OSError: # TODO, report api @@ -4015,7 +4086,7 @@ def _test_wait(): print("Command line arguments passed: " + str(extra_args)) #return True self.update_stats("", "POV-Ray 3.7: Parsing File") - + # Indented in main function now so repeated here but still not working @@ -4040,7 +4111,7 @@ def _test_wait(): print(f.read()) self.update_stats("", "") - + if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable: self._cleanup() else: @@ -4394,7 +4465,7 @@ def execute(self, context): bpy.ops.render.render() - + #empty text name property engain scene.pov.text_block = "" - return {'FINISHED'} \ No newline at end of file + return {'FINISHED'} \ No newline at end of file diff --git a/release/scripts/addons/render_povray/shading.py b/release/scripts/addons/render_povray/shading.py index 5a37170753f5..03a63a9d299c 100644 --- a/release/scripts/addons/render_povray/shading.py +++ b/release/scripts/addons/render_povray/shading.py @@ -245,13 +245,11 @@ def povHasnoSpecularMaps(Level): for t in material.texture_slots: if t and t.use and t.texture is not None: if (t.texture.type == 'IMAGE' and t.texture.image) or t.texture.type != 'IMAGE': - validPath=True - else: - validPath=False - if(t and t.use and validPath and - (t.use_map_specular or t.use_map_raymir or t.use_map_normal or t.use_map_alpha)): - special_texture_found = True - continue # Some texture found + #validPath + if(t and t.use and + (t.use_map_specular or t.use_map_raymir or t.use_map_normal or t.use_map_alpha)): + special_texture_found = True + continue # Some texture found if special_texture_found or colored_specular_found: # Level=1 Means No specular nor Mirror reflection @@ -1087,7 +1085,6 @@ def writeTextureInfluence(mater, materialNames, LocalMaterialNames, path_image, else: if texturesDif and texturesDif.startswith("PAT_"): tabWrite("pigment{%s %s}\n" %(texturesDif, mappingDif)) - print('XXXMEEEERDE!') else: tabWrite("pigment {\n") tabWrite("uv_mapping image_map {\n") diff --git a/release/scripts/addons/render_povray/ui.py b/release/scripts/addons/render_povray/ui.py index 393796ed2bb8..58c693983729 100644 --- a/release/scripts/addons/render_povray/ui.py +++ b/release/scripts/addons/render_povray/ui.py @@ -22,6 +22,7 @@ import sys #really import here and in render.py? import os #really import here and in render.py? from os.path import isfile +from bl_operators.presets import AddPresetBase # Use some of the existing buttons. from bl_ui import properties_render @@ -41,6 +42,61 @@ properties_world.WORLD_PT_mist.COMPAT_ENGINES.add('POVRAY_RENDER') del properties_world +class POV_WORLD_MT_presets(bpy.types.Menu): + bl_label = "World Presets" + preset_subdir = "pov/world" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class AddPresetWorld(AddPresetBase, bpy.types.Operator): + '''Add a World Preset''' + bl_idname = "object.world_preset_add" + bl_label = "Add World Preset" + preset_menu = "POV_WORLD_MT_presets" + + # variable used for all preset values + preset_defines = [ + "scene = bpy.context.scene" + ] + + # properties to store in the preset + preset_values = [ + "scene.world.use_sky_blend", + "scene.world.horizon_color", + "scene.world.zenith_color", + "scene.world.ambient_color", + "scene.world.mist_settings.use_mist", + "scene.world.mist_settings.intensity", + "scene.world.mist_settings.depth", + "scene.world.mist_settings.start", + "scene.pov.media_enable", + "scene.pov.media_scattering_type", + "scene.pov.media_samples", + "scene.pov.media_diffusion_scale", + "scene.pov.media_diffusion_color", + "scene.pov.media_absorption_scale", + "scene.pov.media_absorption_color", + "scene.pov.media_eccentricity", + ] + + # where to store the preset + preset_subdir = "pov/world" + +# Draw into an existing panel +def world_panel_func(self, context): + layout = self.layout + + row = layout.row(align=True) + row.menu(POV_WORLD_MT_presets.__name__, text=POV_WORLD_MT_presets.bl_label) + row.operator(AddPresetWorld.bl_idname, text="", icon='ZOOMIN') + row.operator(AddPresetWorld.bl_idname, text="", icon='ZOOMOUT').remove_active = True + + +classes = ( + POV_WORLD_MT_presets, + AddPresetWorld, + ) # Example of wrapping every class 'as is' from bl_ui import properties_texture @@ -453,6 +509,52 @@ class LAMP_PT_POV_lamp(PovLampButtonsPanel, bpy.types.Panel): draw = properties_data_lamp.DATA_PT_lamp.draw +class POV_LAMP_MT_presets(bpy.types.Menu): + bl_label = "Lamp Presets" + preset_subdir = "pov/lamp" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class AddPresetLamp(AddPresetBase, bpy.types.Operator): + '''Add a Lamp Preset''' + bl_idname = "object.lamp_preset_add" + bl_label = "Add Lamp Preset" + preset_menu = "POV_LAMP_MT_presets" + + # variable used for all preset values + preset_defines = [ + "lampdata = bpy.context.object.data" + ] + + # properties to store in the preset + preset_values = [ + "lampdata.type", + "lampdata.color", + ] + + # where to store the preset + preset_subdir = "pov/lamp" + + + + + +# Draw into an existing panel +def lamp_panel_func(self, context): + layout = self.layout + + row = layout.row(align=True) + row.menu(POV_LAMP_MT_presets.__name__, text=POV_LAMP_MT_presets.bl_label) + row.operator(AddPresetLamp.bl_idname, text="", icon='ZOOMIN') + row.operator(AddPresetLamp.bl_idname, text="", icon='ZOOMOUT').remove_active = True + + +classes = ( + POV_LAMP_MT_presets, + AddPresetLamp, + ) + class LAMP_PT_POV_sunsky(PovLampButtonsPanel, bpy.types.Panel): bl_label = properties_data_lamp.DATA_PT_sunsky.bl_label @@ -609,7 +711,10 @@ def draw(self, context): scene = context.scene #layout.active = (scene.pov.max_trace_level != 0) - + + if sys.platform[:3] != "win": + layout.prop(scene.pov, "sdl_window_enable", text="POV-Ray SDL Window") + col = layout.column() col.label(text="Global Settings:") @@ -787,7 +892,71 @@ def draw(self, context): col.prop(scene.pov, "radio_subsurface") - + + + +class POV_RADIOSITY_MT_presets(bpy.types.Menu): + bl_label = "Radiosity Presets" + preset_subdir = "pov/radiosity" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class AddPresetRadiosity(AddPresetBase, bpy.types.Operator): + '''Add a Radiosity Preset''' + bl_idname = "scene.radiosity_preset_add" + bl_label = "Add Radiosity Preset" + preset_menu = "POV_RADIOSITY_MT_presets" + + # variable used for all preset values + preset_defines = [ + "scene = bpy.context.scene" + ] + + # properties to store in the preset + preset_values = [ + "scene.pov.radio_display_advanced", + "scene.pov.radio_adc_bailout", + "scene.pov.radio_always_sample", + "scene.pov.radio_brightness", + "scene.pov.radio_count", + "scene.pov.radio_error_bound", + "scene.pov.radio_gray_threshold", + "scene.pov.radio_low_error_factor", + "scene.pov.radio_media", + "scene.pov.radio_subsurface", + "scene.pov.radio_minimum_reuse", + "scene.pov.radio_maximum_reuse", + "scene.pov.radio_nearest_count", + "scene.pov.radio_normal", + "scene.pov.radio_recursion_limit", + "scene.pov.radio_pretrace_start", + "scene.pov.radio_pretrace_end", + ] + + # where to store the preset + preset_subdir = "pov/radiosity" + + + + + +# Draw into an existing panel +def rad_panel_func(self, context): + layout = self.layout + + row = layout.row(align=True) + row.menu(POV_RADIOSITY_MT_presets.__name__, text=POV_RADIOSITY_MT_presets.bl_label) + row.operator(AddPresetRadiosity.bl_idname, text="", icon='ZOOMIN') + row.operator(AddPresetRadiosity.bl_idname, text="", icon='ZOOMOUT').remove_active = True + + +classes = ( + POV_RADIOSITY_MT_presets, + AddPresetRadiosity, + ) + + class RENDER_PT_povray_media(WorldButtonsPanel, bpy.types.Panel): bl_label = "Atmosphere Media" COMPAT_ENGINES = {'POVRAY_RENDER'} @@ -804,10 +973,22 @@ def draw(self, context): layout.active = scene.pov.media_enable - row = layout.row() - row.prop(scene.pov, "media_samples", text="Samples") - row.prop(scene.pov, "media_color", text="") - + col = layout.column() + col.prop(scene.pov, "media_scattering_type", text="") + col = layout.column() + col.prop(scene.pov, "media_samples", text="Samples") + split = layout.split() + col = split.column(align=True) + col.label(text="Scattering:") + col.prop(scene.pov, "media_diffusion_scale") + col.prop(scene.pov, "media_diffusion_color", text="") + col = split.column(align=True) + col.label(text="Absorption:") + col.prop(scene.pov, "media_absorption_scale") + col.prop(scene.pov, "media_absorption_color", text="") + if scene.pov.media_scattering_type == '5': + col = layout.column() + col.prop(scene.pov, "media_eccentricity", text="Eccentricity") ##class RENDER_PT_povray_baking(RenderButtonsPanel, bpy.types.Panel): ## bl_label = "Baking" ## COMPAT_ENGINES = {'POVRAY_RENDER'} @@ -824,7 +1005,7 @@ def draw(self, context): ## rd = scene.render ## ## layout.active = scene.pov.baking_enable -'''XXX WIP preparing for CSG + class MODIFIERS_PT_povray_modifiers(ModifierButtonsPanel, bpy.types.Panel): bl_label = "POV-Ray" COMPAT_ENGINES = {'POVRAY_RENDER'} @@ -853,7 +1034,7 @@ def draw(self, context): col = layout.column() # Inside Vector for CSG col.prop(ob.pov, "inside_vector") -''' + class MATERIAL_PT_povray_activate_node(MaterialButtonsPanel, bpy.types.Panel): bl_label = "Activate Node Settings" @@ -1678,9 +1859,9 @@ def draw(self, context): ############################################################################### -class Povray_primitives_add_menu(bpy.types.Menu): +class POVRAY_MT_primitives_add_menu(bpy.types.Menu): """Define the menu with presets""" - bl_idname = "Povray_primitives_add_menu" + bl_idname = "POVRAY_MT_primitives_add_menu" bl_label = "Povray" COMPAT_ENGINES = {'POVRAY_RENDER'} @@ -1696,11 +1877,10 @@ def draw(self,context): layout.menu(ImportMenu.bl_idname, text = "Import",icon="IMPORT") class BasicShapesMenu(bpy.types.Menu): - bl_idname = "Basic_shapes_calls" + bl_idname = "POVRAY_MT_basic_shape_tools" bl_label = "Basic_shapes" def draw(self,context): - pov = bpy.types.Object.pov #context.object.pov ? layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("pov.addplane", text="Infinite Plane",icon = 'MESH_PLANE') @@ -1744,11 +1924,10 @@ def draw(self,context): layout.operator("pov.addparametric", text="Parametric",icon = 'SCRIPTPLUGINS') class ImportMenu(bpy.types.Menu): - bl_idname = "Importer_calls" + bl_idname = "POVRAY_MT_import_tools" bl_label = "Import" def draw(self,context): - pov = bpy.types.Object.pov #context.object.pov ? layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("import_scene.pov",icon="FORCE_LENNARDJONES") @@ -1756,7 +1935,7 @@ def draw(self,context): def menu_func_add(self, context): engine = context.scene.render.engine if engine == 'POVRAY_RENDER': - self.layout.menu("Povray_primitives_add_menu", icon="PLUGIN") + self.layout.menu("POVRAY_MT_primitives_add_menu", icon="PLUGIN") def menu_func_import(self, context): engine = context.scene.render.engine @@ -1790,9 +1969,9 @@ def menu_func_import(self, context): # return True -class Node_map_create_menu(bpy.types.Menu): +class NodeMapCreateMenu(bpy.types.Menu): """Create maps""" - bl_idname = "Node_map_create_menu" + bl_idname = "POVRAY_MT_node_map_create" bl_label = "Create map" def draw(self,context): @@ -1805,7 +1984,7 @@ def menu_func_nodes(self, context): mat=context.object.active_material if mat and context.space_data.tree_type == 'ObjectNodeTree': self.layout.prop(mat.pov,"material_use_nodes") - self.layout.menu("Node_map_create_menu") + self.layout.menu(NodeMapCreateMenu.bl_idname) self.layout.operator("wm.updatepreviewkey") if hasattr(mat,'active_texture') and context.scene.render.engine == 'POVRAY_RENDER': tex=mat.active_texture diff --git a/release/scripts/addons/rigify/legacy/rigs/biped/limb_common.py b/release/scripts/addons/rigify/legacy/rigs/biped/limb_common.py index eadd63749ef8..73e9ad342226 100644 --- a/release/scripts/addons/rigify/legacy/rigs/biped/limb_common.py +++ b/release/scripts/addons/rigify/legacy/rigs/biped/limb_common.py @@ -1080,20 +1080,20 @@ def generate(self): # B-bone settings ulimb2_p.bone.bbone_segments = 16 - ulimb2_p.bone.bbone_in = 0.0 - ulimb2_p.bone.bbone_out = 1.0 + ulimb2_p.bone.bbone_easein = 0.0 + ulimb2_p.bone.bbone_easeout = 1.0 ulimb2_smoother_p.bone.bbone_segments = 16 - ulimb2_smoother_p.bone.bbone_in = 1.0 - ulimb2_smoother_p.bone.bbone_out = 0.0 + ulimb2_smoother_p.bone.bbone_easein = 1.0 + ulimb2_smoother_p.bone.bbone_easeout = 0.0 flimb1_p.bone.bbone_segments = 16 - flimb1_p.bone.bbone_in = 1.0 - flimb1_p.bone.bbone_out = 0.0 + flimb1_p.bone.bbone_easein = 1.0 + flimb1_p.bone.bbone_easeout = 0.0 flimb1_smoother_p.bone.bbone_segments = 16 - flimb1_smoother_p.bone.bbone_in = 0.0 - flimb1_smoother_p.bone.bbone_out = 1.0 + flimb1_smoother_p.bone.bbone_easein = 0.0 + flimb1_smoother_p.bone.bbone_easeout = 1.0 # Custom properties prop = rna_idprop_ui_prop_get(jhose_p, "smooth_bend", create=True) diff --git a/release/scripts/addons/space_view3d_math_vis/draw.py b/release/scripts/addons/space_view3d_math_vis/draw.py index 1875afcd2f50..8a8cfa7ac877 100644 --- a/release/scripts/addons/space_view3d_math_vis/draw.py +++ b/release/scripts/addons/space_view3d_math_vis/draw.py @@ -113,7 +113,7 @@ def draw_text(text, vec, dx=3.0, dy=-4.0): # lines if data_vector_array: for key, vec in data_vector_array.items(): - if vec and len(vec) > 0: + if vec: draw_text(key, vec[0]) # matrix diff --git a/release/scripts/addons/uv_magic_uv/__init__.py b/release/scripts/addons/uv_magic_uv/__init__.py index 6b9b6d2ba453..080d2414fbf5 100644 --- a/release/scripts/addons/uv_magic_uv/__init__.py +++ b/release/scripts/addons/uv_magic_uv/__init__.py @@ -20,18 +20,18 @@ __author__ = "Nutti " __status__ = "production" -__version__ = "4.4" -__date__ = "2 Aug 2017" +__version__ = "5.1" +__date__ = "24 Feb 2018" bl_info = { "name": "Magic UV", - "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, " - "Keith (Wahooney) Boshoff, McBuff, MaxRobinot", - "version": (4, 4, 0), + "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, imdjs" + "Keith (Wahooney) Boshoff, McBuff, MaxRobinot, Alexander Milovsky", + "version": (5, 1, 0), "blender": (2, 79, 0), "location": "See Add-ons Preferences", - "description": "UV Manipulator Tools. See Add-ons Preferences for details", + "description": "UV Toolset. See Add-ons Preferences for details", "warning": "", "support": "COMMUNITY", "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/" @@ -42,94 +42,29 @@ if "bpy" in locals(): import importlib - importlib.reload(muv_preferences) - importlib.reload(muv_menu) - importlib.reload(muv_common) - importlib.reload(muv_props) - importlib.reload(muv_cpuv_ops) - importlib.reload(muv_cpuv_selseq_ops) - importlib.reload(muv_fliprot_ops) - importlib.reload(muv_transuv_ops) - importlib.reload(muv_uvbb_ops) - importlib.reload(muv_mvuv_ops) - importlib.reload(muv_texproj_ops) - importlib.reload(muv_packuv_ops) - importlib.reload(muv_texlock_ops) - importlib.reload(muv_mirroruv_ops) - importlib.reload(muv_wsuv_ops) - importlib.reload(muv_unwrapconst_ops) - importlib.reload(muv_preserve_uv_aspect) + importlib.reload(op) + importlib.reload(ui) + importlib.reload(common) + importlib.reload(preferences) + importlib.reload(properites) else: - from . import muv_preferences - from . import muv_menu - from . import muv_common - from . import muv_props - from . import muv_cpuv_ops - from . import muv_cpuv_selseq_ops - from . import muv_fliprot_ops - from . import muv_transuv_ops - from . import muv_uvbb_ops - from . import muv_mvuv_ops - from . import muv_texproj_ops - from . import muv_packuv_ops - from . import muv_texlock_ops - from . import muv_mirroruv_ops - from . import muv_wsuv_ops - from . import muv_unwrapconst_ops - from . import muv_preserve_uv_aspect + from . import op + from . import ui + from . import common + from . import preferences + from . import properites import bpy -def view3d_uvmap_menu_fn(self, context): - self.layout.separator() - self.layout.menu(muv_menu.MUV_CPUVMenu.bl_idname, icon="IMAGE_COL") - self.layout.operator(muv_fliprot_ops.MUV_FlipRot.bl_idname, icon="IMAGE_COL") - self.layout.menu(muv_menu.MUV_TransUVMenu.bl_idname, icon="IMAGE_COL") - self.layout.operator(muv_mvuv_ops.MUV_MVUV.bl_idname, icon="IMAGE_COL") - self.layout.menu(muv_menu.MUV_TexLockMenu.bl_idname, icon="IMAGE_COL") - self.layout.operator( - muv_mirroruv_ops.MUV_MirrorUV.bl_idname, icon="IMAGE_COL") - self.layout.menu(muv_menu.MUV_WSUVMenu.bl_idname, icon="IMAGE_COL") - self.layout.operator( - muv_unwrapconst_ops.MUV_UnwrapConstraint.bl_idname, icon='IMAGE_COL') - self.layout.menu( - muv_preserve_uv_aspect.MUV_PreserveUVAspectMenu.bl_idname, - icon='IMAGE_COL') - - -def image_uvs_menu_fn(self, context): - self.layout.separator() - self.layout.operator(muv_packuv_ops.MUV_PackUV.bl_idname, icon="IMAGE_COL") - - -def view3d_object_menu_fn(self, context): - self.layout.separator() - self.layout.menu(muv_menu.MUV_CPUVObjMenu.bl_idname, icon="IMAGE_COL") - - def register(): bpy.utils.register_module(__name__) - bpy.types.VIEW3D_MT_uv_map.append(view3d_uvmap_menu_fn) - bpy.types.IMAGE_MT_uvs.append(image_uvs_menu_fn) - bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn) - try: - bpy.types.VIEW3D_MT_Object.append(view3d_object_menu_fn) - except: - pass - muv_props.init_props(bpy.types.Scene) + properites.init_props(bpy.types.Scene) def unregister(): bpy.utils.unregister_module(__name__) - bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn) - bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn) - bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn) - try: - bpy.types.VIEW3D_MT_Object.remove(view3d_object_menu_fn) - except: - pass - muv_props.clear_props(bpy.types.Scene) + properites.clear_props(bpy.types.Scene) if __name__ == "__main__":