diff options
Diffstat (limited to 'release/scripts/startup')
41 files changed, 1671 insertions, 704 deletions
diff --git a/release/scripts/startup/bl_operators/add_mesh_torus.py b/release/scripts/startup/bl_operators/add_mesh_torus.py index 460330a56a1..27a6d21d519 100644 --- a/release/scripts/startup/bl_operators/add_mesh_torus.py +++ b/release/scripts/startup/bl_operators/add_mesh_torus.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy import mathutils @@ -40,8 +40,10 @@ def add_torus(major_rad, minor_rad, major_seg, minor_seg): for minor_index in range(minor_seg): angle = 2 * pi * minor_index / minor_seg - vec = Vector((major_rad + (cos(angle) * minor_rad), 0.0, - (sin(angle) * minor_rad))) * quat + vec = quat * Vector((major_rad + (cos(angle) * minor_rad), + 0.0, + (sin(angle) * minor_rad), + )) verts.extend(vec[:]) @@ -72,7 +74,11 @@ def add_torus(major_rad, minor_rad, major_seg, minor_seg): return verts, faces -from bpy.props import FloatProperty, IntProperty, BoolProperty, FloatVectorProperty +from bpy.props import (FloatProperty, + IntProperty, + BoolProperty, + FloatVectorProperty, + ) class AddTorus(bpy.types.Operator): @@ -82,7 +88,8 @@ class AddTorus(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} major_radius = FloatProperty(name="Major Radius", - description="Radius from the origin to the center of the cross sections", + description=("Radius from the origin to the " + "center of the cross sections"), default=1.0, min=0.01, max=100.0) minor_radius = FloatProperty(name="Minor Radius", description="Radius of the torus' cross section", @@ -132,7 +139,7 @@ class AddTorus(bpy.types.Operator): mesh.faces.foreach_set("vertices_raw", faces) mesh.update() - import add_object_utils - add_object_utils.object_data_add(context, mesh, operator=self) + from bpy_extras import object_utils + object_utils.object_data_add(context, mesh, operator=self) return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/image.py b/release/scripts/startup/bl_operators/image.py index 462db3a2c5e..23bafe2eaae 100644 --- a/release/scripts/startup/bl_operators/image.py +++ b/release/scripts/startup/bl_operators/image.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy from bpy.props import StringProperty @@ -28,7 +28,11 @@ class EditExternally(bpy.types.Operator): bl_label = "Image Edit Externally" bl_options = {'REGISTER'} - filepath = StringProperty(name="File Path", description="Path to an image file", maxlen=1024, default="") + filepath = StringProperty( + name="File Path", + description="Path to an image file", + maxlen=1024, + ) def _editor_guess(self, context): import sys @@ -57,10 +61,13 @@ class EditExternally(bpy.types.Operator): def execute(self, context): import os import subprocess - filepath = bpy.path.abspath(self.filepath) + filepath = os.path.normpath(bpy.path.abspath(self.filepath)) if not os.path.exists(filepath): - self.report({'ERROR'}, "Image path %r not found." % filepath) + self.report({'ERROR'}, + "Image path %r not found, image may be packed or " + "unsaved." % filepath) + return {'CANCELLED'} cmd = self._editor_guess(context) + [filepath] @@ -70,7 +77,10 @@ class EditExternally(bpy.types.Operator): except: import traceback traceback.print_exc() - self.report({'ERROR'}, "Image editor not found, please specify in User Preferences > File") + self.report({'ERROR'}, + "Image editor not found, " + "please specify in User Preferences > File") + return {'CANCELLED'} return {'FINISHED'} @@ -104,7 +114,9 @@ class SaveDirty(bpy.types.Operator): if "\\" not in filepath and "/" not in filepath: self.report({'WARNING'}, "Invalid path: " + filepath) elif filepath in unique_paths: - self.report({'WARNING'}, "Path used by more then one image: " + filepath) + self.report({'WARNING'}, + "Path used by more then one image: %r" % + filepath) else: unique_paths.add(filepath) image.save() @@ -121,7 +133,6 @@ class ProjectEdit(bpy.types.Operator): def execute(self, context): import os - import subprocess EXT = "png" # could be made an option but for now ok @@ -143,14 +154,14 @@ class ProjectEdit(bpy.types.Operator): filepath = os.path.basename(bpy.data.filepath) filepath = os.path.splitext(filepath)[0] - # filepath = bpy.path.clean_name(filepath) # fixes <memory> rubbish, needs checking + # fixes <memory> rubbish, needs checking + # filepath = bpy.path.clean_name(filepath) - if filepath.startswith(".") or filepath == "": - # TODO, have a way to check if the file is saved, assume startup.blend + if bpy.data.is_saved: + filepath = "//" + filepath + else: tmpdir = context.user_preferences.filepaths.temporary_directory filepath = os.path.join(tmpdir, "project_edit") - else: - filepath = "//" + filepath obj = context.object @@ -164,7 +175,7 @@ class ProjectEdit(bpy.types.Operator): filepath_final = filepath + ("%.3d.%s" % (i, EXT)) i += 1 - image_new.name = os.path.basename(filepath_final) + image_new.name = bpy.path.basename(filepath_final) ProjectEdit._proj_hack[0] = image_new.name image_new.filepath_raw = filepath_final # TODO, filepath raw is crummy diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py index 44d81ba53df..344b238709f 100644 --- a/release/scripts/startup/bl_operators/mesh.py +++ b/release/scripts/startup/bl_operators/mesh.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy @@ -36,6 +36,7 @@ class MeshSelectInteriorFaces(bpy.types.Operator): return (ob and ob.type == 'MESH') def execute(self, context): + from bpy_extras import mesh_utils ob = context.active_object context.tool_settings.mesh_select_mode = False, False, True is_editmode = (ob.mode == 'EDIT') @@ -47,7 +48,7 @@ class MeshSelectInteriorFaces(bpy.types.Operator): face_list = mesh.faces[:] face_edge_keys = [face.edge_keys for face in face_list] - edge_face_count = mesh.edge_face_count_dict + edge_face_count = mesh_utils.edge_face_count_dict(mesh) def test_interior(index): for key in face_edge_keys[index]: @@ -80,14 +81,12 @@ class MeshMirrorUV(bpy.types.Operator): @classmethod def poll(cls, context): - ob = context.active_object - return (ob and ob.type == 'MESH') + obj = context.active_object + return (obj and obj.type == 'MESH' and obj.data.uv_textures.active) def execute(self, context): DIR = (self.direction == 'NEGATIVE') - from mathutils import Vector - ob = context.active_object is_editmode = (ob.mode == 'EDIT') if is_editmode: @@ -112,21 +111,18 @@ class MeshMirrorUV(bpy.types.Operator): #for i, v in enumerate(mesh.vertices): vmap = {} - for mirror_a, mirror_b in (mirror_gt, mirror_lt), (mirror_lt, mirror_gt): + for mirror_a, mirror_b in ((mirror_gt, mirror_lt), + (mirror_lt, mirror_gt)): for co, i in mirror_a.items(): nco = (-co[0], co[1], co[2]) j = mirror_b.get(nco) if j is not None: vmap[i] = j - active_uv_layer = None - for lay in mesh.uv_textures: - if lay.active: - active_uv_layer = lay.data - break - + active_uv_layer = mesh.uv_textures.active.data fuvs = [(uv.uv1, uv.uv2, uv.uv3, uv.uv4) for uv in active_uv_layer] - fuvs_cpy = [(uv[0].copy(), uv[1].copy(), uv[2].copy(), uv[3].copy()) for uv in fuvs] + fuvs_cpy = [(uv[0].copy(), uv[1].copy(), uv[2].copy(), uv[3].copy()) + for uv in fuvs] # as a list faces = mesh.faces[:] @@ -151,7 +147,6 @@ class MeshMirrorUV(bpy.types.Operator): if j is not None: fmap[i] = j - done = [False] * len(faces) for i, j in fmap.items(): if not fuvsel[i] or not fuvsel[j]: @@ -169,10 +164,10 @@ class MeshMirrorUV(bpy.types.Operator): v1 = faces[j].vertices[:] v2 = [vmap[k] for k in faces[i].vertices[:]] - for k in range(len(uv1)): - k_map = v1.index(v2[k]) - uv1[k].x = - (uv2[k_map].x - 0.5) + 0.5 - uv1[k].y = uv2[k_map].y + if len(v1) == len(v2): + for k in range(len(v1)): + k_map = v1.index(v2[k]) + uv1[k].xy = - (uv2[k_map].x - 0.5) + 0.5, uv2[k_map].y if is_editmode: bpy.ops.object.mode_set(mode='EDIT', toggle=False) diff --git a/release/scripts/startup/bl_operators/nla.py b/release/scripts/startup/bl_operators/nla.py index 923ca92a162..469e9015e62 100644 --- a/release/scripts/startup/bl_operators/nla.py +++ b/release/scripts/startup/bl_operators/nla.py @@ -16,17 +16,16 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy -def pose_info(): +def pose_frame_info(obj): from mathutils import Matrix info = {} - obj = bpy.context.object pose = obj.pose pose_items = pose.bones.items() @@ -51,7 +50,6 @@ def pose_info(): except: binfo["matrix_pose_inv"] = Matrix() - print(binfo["matrix_pose"]) info[name] = binfo for name, pbone in pose_items: @@ -67,45 +65,86 @@ def pose_info(): matrix = binfo_parent["matrix_pose_inv"] * matrix rest_matrix = binfo_parent["matrix_local_inv"] * rest_matrix - matrix = rest_matrix.inverted() * matrix + binfo["matrix_key"] = rest_matrix.inverted() * matrix + + return info - binfo["matrix_key"] = matrix.copy() +def obj_frame_info(obj): + info = {} + # parent = obj.parent + info["matrix_key"] = obj.matrix_local.copy() return info -def bake(frame_start, frame_end, step=1, only_selected=False): +def bake(frame_start, + frame_end, step=1, + only_selected=False, + do_pose=True, + do_object=True, + do_constraint_clear=False, + ): + scene = bpy.context.scene obj = bpy.context.object pose = obj.pose + frame_back = scene.frame_current + + if pose is None: + do_pose = False - info_ls = [] + if do_pose is None and do_object is None: + return None + + pose_info = [] + obj_info = [] frame_range = range(frame_start, frame_end + 1, step) - # could spped this up by applying steps here too... + # ------------------------------------------------------------------------- + # Collect transformations + + # could speed this up by applying steps here too... for f in frame_range: scene.frame_set(f) - info = pose_info() - info_ls.append(info) + if do_pose: + pose_info.append(pose_frame_info(obj)) + if do_object: + obj_info.append(obj_frame_info(obj)) + f += 1 + # ------------------------------------------------------------------------- + # Create action + + # incase animation data hassnt been created + atd = obj.animation_data_create() action = bpy.data.actions.new("Action") + atd.action = action - bpy.context.object.animation_data.action = action + if do_pose: + pose_items = pose.bones.items() + else: + pose_items = [] # skip - pose_items = pose.bones.items() + # ------------------------------------------------------------------------- + # Apply transformations to action - for name, pbone in pose_items: + # pose + for name, pbone in (pose_items if do_pose else ()): if only_selected and not pbone.bone.select: continue + if do_constraint_clear: + while pbone.constraints: + pbone.constraints.remove(pbone.constraints[0]) + for f in frame_range: - matrix = info_ls[int((f - frame_start) / step)][name]["matrix_key"] + matrix = pose_info[(f - frame_start) // step][name]["matrix_key"] - #pbone.location = matrix.to_translation() - #pbone.rotation_quaternion = matrix.to_quaternion() + # pbone.location = matrix.to_translation() + # pbone.rotation_quaternion = matrix.to_quaternion() pbone.matrix_basis = matrix pbone.keyframe_insert("location", -1, f, name) @@ -121,10 +160,35 @@ def bake(frame_start, frame_end, step=1, only_selected=False): pbone.keyframe_insert("scale", -1, f, name) + # object. TODO. multiple objects + if do_object: + if do_constraint_clear: + while obj.constraints: + obj.constraints.remove(obj.constraints[0]) + + for f in frame_range: + matrix = obj_info[(f - frame_start) // step]["matrix_key"] + obj.matrix_local = matrix + + obj.keyframe_insert("location", -1, f) + + rotation_mode = obj.rotation_mode + + if rotation_mode == 'QUATERNION': + obj.keyframe_insert("rotation_quaternion", -1, f) + elif rotation_mode == 'AXIS_ANGLE': + obj.keyframe_insert("rotation_axis_angle", -1, f) + else: # euler, XYZ, ZXY etc + obj.keyframe_insert("rotation_euler", -1, f) + + obj.keyframe_insert("scale", -1, f) + + scene.frame_set(frame_back) + return action -from bpy.props import IntProperty, BoolProperty +from bpy.props import IntProperty, BoolProperty, EnumProperty class BakeAction(bpy.types.Operator): @@ -144,10 +208,31 @@ class BakeAction(bpy.types.Operator): default=1, min=1, max=120) only_selected = BoolProperty(name="Only Selected", default=True) + clear_consraints = BoolProperty(name="Clear Constraints", + default=False) + bake_types = EnumProperty( + name="Bake Data", + options={'ENUM_FLAG'}, + items=(('POSE', "Pose", ""), + ('OBJECT', "Object", ""), + ), + default={'POSE'}, + ) def execute(self, context): - action = bake(self.frame_start, self.frame_end, self.step, self.only_selected) + action = bake(self.frame_start, + self.frame_end, + self.step, + self.only_selected, + 'POSE' in self.bake_types, + 'OBJECT' in self.bake_types, + self.clear_consraints, + ) + + if action is None: + self.report({'INFO'}, "Nothing to bake") + return {'CANCELLED'} # basic cleanup, could move elsewhere for fcu in action.fcurves: diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 0342a14a1b2..627a1530fe1 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy from bpy.props import StringProperty, BoolProperty, EnumProperty, IntProperty @@ -28,9 +28,22 @@ class SelectPattern(bpy.types.Operator): bl_label = "Select Pattern" bl_options = {'REGISTER', 'UNDO'} - pattern = StringProperty(name="Pattern", description="Name filter using '*' and '?' wildcard chars", maxlen=32, default="*") - case_sensitive = BoolProperty(name="Case Sensitive", description="Do a case sensitive compare", default=False) - extend = BoolProperty(name="Extend", description="Extend the existing selection", default=True) + pattern = StringProperty( + name="Pattern", + description="Name filter using '*' and '?' wildcard chars", + maxlen=32, + default="*", + ) + case_sensitive = BoolProperty( + name="Case Sensitive", + description="Do a case sensitive compare", + default=False, + ) + extend = BoolProperty( + name="Extend", + description="Extend the existing selection", + default=True, + ) def execute(self, context): @@ -39,22 +52,37 @@ class SelectPattern(bpy.types.Operator): if self.case_sensitive: pattern_match = fnmatch.fnmatchcase else: - pattern_match = lambda a, b: fnmatch.fnmatchcase(a.upper(), b.upper()) - + pattern_match = (lambda a, b: + fnmatch.fnmatchcase(a.upper(), b.upper())) + is_ebone = False obj = context.object if obj and obj.mode == 'POSE': items = obj.data.bones + if not self.extend: + bpy.ops.pose.select_all(action='DESELECT') elif obj and obj.type == 'ARMATURE' and obj.mode == 'EDIT': items = obj.data.edit_bones + if not self.extend: + bpy.ops.armature.select_all(action='DESELECT') + is_ebone = True else: items = context.visible_objects + if not self.extend: + bpy.ops.object.select_all(action='DESELECT') # Can be pose bones or objects for item in items: if pattern_match(item.name, self.pattern): item.select = True - elif not self.extend: - item.select = False + + # hrmf, perhaps there should be a utility function for this. + if is_ebone: + item.select_head = True + item.select_tail = True + if item.use_connect: + item_parent = item.parent + if item_parent is not None: + item_parent.select_tail = True return {'FINISHED'} @@ -93,19 +121,25 @@ class SelectCamera(bpy.types.Operator): class SelectHierarchy(bpy.types.Operator): - '''Select object relative to the active objects position in the hierarchy''' + '''Select object relative to the active objects position''' \ + '''in the hierarchy''' bl_idname = "object.select_hierarchy" bl_label = "Select Hierarchy" bl_options = {'REGISTER', 'UNDO'} - direction = EnumProperty(items=( - ('PARENT', "Parent", ""), - ('CHILD', "Child", "")), - name="Direction", - description="Direction to select in the hierarchy", - default='PARENT') + direction = EnumProperty( + items=(('PARENT', "Parent", ""), + ('CHILD', "Child", ""), + ), + name="Direction", + description="Direction to select in the hierarchy", + default='PARENT') - extend = BoolProperty(name="Extend", description="Extend the existing selection", default=False) + extend = BoolProperty( + name="Extend", + description="Extend the existing selection", + default=False, + ) @classmethod def poll(cls, context): @@ -163,7 +197,12 @@ class SubdivisionSet(bpy.types.Operator): level = IntProperty(name="Level", default=1, min=-100, max=100, soft_min=-6, soft_max=6) - relative = BoolProperty(name="Relative", description="Apply the subsurf level as an offset relative to the current level", default=False) + relative = BoolProperty( + name="Relative", + description=("Apply the subsurf level as an offset " + "relative to the current level"), + default=False, + ) @classmethod def poll(cls, context): @@ -215,7 +254,8 @@ class SubdivisionSet(bpy.types.Operator): mod = obj.modifiers.new("Subsurf", 'SUBSURF') mod.levels = level except: - self.report({'WARNING'}, "Modifiers cannot be added to object: " + obj.name) + self.report({'WARNING'}, + "Modifiers cannot be added to object: " + obj.name) for obj in context.selected_editable_objects: set_object_subd(obj) @@ -224,23 +264,37 @@ class SubdivisionSet(bpy.types.Operator): class ShapeTransfer(bpy.types.Operator): - '''Copy another selected objects active shape to this one by applying the relative offsets''' + '''Copy another selected objects active shape to this one by ''' \ + '''applying the relative offsets''' bl_idname = "object.shape_key_transfer" bl_label = "Transfer Shape Key" bl_options = {'REGISTER', 'UNDO'} - mode = EnumProperty(items=( - ('OFFSET', "Offset", "Apply the relative positional offset"), - ('RELATIVE_FACE', "Relative Face", "Calculate the geometricly relative position (using faces)."), - ('RELATIVE_EDGE', "Relative Edge", "Calculate the geometricly relative position (using edges).")), - name="Transformation Mode", - description="Method to apply relative shape positions to the new shape", - default='OFFSET') - - use_clamp = BoolProperty(name="Clamp Offset", - description="Clamp the transformation to the distance each vertex moves in the original shape.", - default=False) + mode = EnumProperty( + items=(('OFFSET', + "Offset", + "Apply the relative positional offset", + ), + ('RELATIVE_FACE', + "Relative Face", + "Calculate relative position (using faces).", + ), + ('RELATIVE_EDGE', + "Relative Edge", + "Calculate relative position (using edges).", + ), + ), + name="Transformation Mode", + description="Relative shape positions to the new shape method", + default='OFFSET', + ) + use_clamp = BoolProperty( + name="Clamp Offset", + description=("Clamp the transformation to the distance each " + "vertex moves in the original shape."), + default=False, + ) def _main(self, ob_act, objects, mode='OFFSET', use_clamp=False): @@ -272,13 +326,16 @@ class ShapeTransfer(bpy.types.Operator): orig_shape_coords = me_cos(ob_act.active_shape_key.data) orig_normals = me_nos(me.vertices) - # orig_coords = me_cos(me.vertices) # the actual mverts location isnt as relyable as the base shape :S + # the actual mverts location isnt as relyable as the base shape :S + # orig_coords = me_cos(me.vertices) orig_coords = me_cos(me.shape_keys.key_blocks[0].data) for ob_other in objects: me_other = ob_other.data if len(me_other.vertices) != len(me.vertices): - self.report({'WARNING'}, "Skipping '%s', vertex count differs" % ob_other.name) + self.report({'WARNING'}, + ("Skipping '%s', " + "vertex count differs") % ob_other.name) continue target_normals = me_nos(me_other.vertices) @@ -290,53 +347,90 @@ class ShapeTransfer(bpy.types.Operator): ob_add_shape(ob_other, orig_key_name) # editing the final coords, only list that stores wrapped coords - target_shape_coords = [v.co for v in ob_other.active_shape_key.data] + target_shape_coords = [v.co for v in + ob_other.active_shape_key.data] median_coords = [[] for i in range(len(me.vertices))] # Method 1, edge if mode == 'OFFSET': for i, vert_cos in enumerate(median_coords): - vert_cos.append(target_coords[i] + (orig_shape_coords[i] - orig_coords[i])) + vert_cos.append(target_coords[i] + + (orig_shape_coords[i] - orig_coords[i])) elif mode == 'RELATIVE_FACE': for face in me.faces: i1, i2, i3, i4 = face.vertices_raw if i4 != 0: pt = barycentric_transform(orig_shape_coords[i1], - orig_coords[i4], orig_coords[i1], orig_coords[i2], - target_coords[i4], target_coords[i1], target_coords[i2]) + orig_coords[i4], + orig_coords[i1], + orig_coords[i2], + target_coords[i4], + target_coords[i1], + target_coords[i2], + ) median_coords[i1].append(pt) pt = barycentric_transform(orig_shape_coords[i2], - orig_coords[i1], orig_coords[i2], orig_coords[i3], - target_coords[i1], target_coords[i2], target_coords[i3]) + orig_coords[i1], + orig_coords[i2], + orig_coords[i3], + target_coords[i1], + target_coords[i2], + target_coords[i3], + ) median_coords[i2].append(pt) pt = barycentric_transform(orig_shape_coords[i3], - orig_coords[i2], orig_coords[i3], orig_coords[i4], - target_coords[i2], target_coords[i3], target_coords[i4]) + orig_coords[i2], + orig_coords[i3], + orig_coords[i4], + target_coords[i2], + target_coords[i3], + target_coords[i4], + ) median_coords[i3].append(pt) pt = barycentric_transform(orig_shape_coords[i4], - orig_coords[i3], orig_coords[i4], orig_coords[i1], - target_coords[i3], target_coords[i4], target_coords[i1]) + orig_coords[i3], + orig_coords[i4], + orig_coords[i1], + target_coords[i3], + target_coords[i4], + target_coords[i1], + ) median_coords[i4].append(pt) else: pt = barycentric_transform(orig_shape_coords[i1], - orig_coords[i3], orig_coords[i1], orig_coords[i2], - target_coords[i3], target_coords[i1], target_coords[i2]) + orig_coords[i3], + orig_coords[i1], + orig_coords[i2], + target_coords[i3], + target_coords[i1], + target_coords[i2], + ) median_coords[i1].append(pt) pt = barycentric_transform(orig_shape_coords[i2], - orig_coords[i1], orig_coords[i2], orig_coords[i3], - target_coords[i1], target_coords[i2], target_coords[i3]) + orig_coords[i1], + orig_coords[i2], + orig_coords[i3], + target_coords[i1], + target_coords[i2], + target_coords[i3], + ) median_coords[i2].append(pt) pt = barycentric_transform(orig_shape_coords[i3], - orig_coords[i2], orig_coords[i3], orig_coords[i1], - target_coords[i2], target_coords[i3], target_coords[i1]) + orig_coords[i2], + orig_coords[i3], + orig_coords[i1], + target_coords[i2], + target_coords[i3], + target_coords[i1], + ) median_coords[i3].append(pt) elif mode == 'RELATIVE_EDGE': @@ -374,7 +468,8 @@ class ShapeTransfer(bpy.types.Operator): if use_clamp: # clamp to the same movement as the original # breaks copy between different scaled meshes. - len_from = (orig_shape_coords[i] - orig_coords[i]).length + len_from = (orig_shape_coords[i] - + orig_coords[i]).length ofs = co - target_coords[i] ofs.length = len_from co = target_coords[i] + ofs @@ -395,7 +490,10 @@ class ShapeTransfer(bpy.types.Operator): if 1: # swap from/to, means we cant copy to many at once. if len(objects) != 1: - self.report({'ERROR'}, "Expected one other selected mesh object to copy from") + self.report({'ERROR'}, + ("Expected one other selected " + "mesh object to copy from")) + return {'CANCELLED'} ob_act, objects = objects[0], [ob_act] @@ -429,11 +527,14 @@ class JoinUVs(bpy.types.Operator): bpy.ops.object.mode_set(mode='OBJECT', toggle=False) if not mesh.uv_textures: - self.report({'WARNING'}, "Object: %s, Mesh: '%s' has no UVs\n" % (obj.name, mesh.name)) + self.report({'WARNING'}, + "Object: %s, Mesh: '%s' has no UVs" + % (obj.name, mesh.name)) else: len_faces = len(mesh.faces) - uv_array = array.array('f', [0.0] * 8) * len_faces # seems to be the fastest way to create an array + # seems to be the fastest way to create an array + uv_array = array.array('f', [0.0] * 8) * len_faces mesh.uv_textures.active.data.foreach_get("uv_raw", uv_array) objects = context.selected_editable_objects[:] @@ -450,11 +551,18 @@ class JoinUVs(bpy.types.Operator): mesh_other.tag = True if len(mesh_other.faces) != len_faces: - self.report({'WARNING'}, "Object: %s, Mesh: '%s' has %d faces, expected %d\n" % (obj_other.name, mesh_other.name, len(mesh_other.faces), len_faces)) + self.report({'WARNING'}, "Object: %s, Mesh: " + "'%s' has %d faces, expected %d\n" + % (obj_other.name, + mesh_other.name, + len(mesh_other.faces), + len_faces), + ) else: uv_other = mesh_other.uv_textures.active if not uv_other: - uv_other = mesh_other.uv_textures.new() # should return the texture it adds + # should return the texture it adds + uv_other = mesh_other.uv_textures.new() # finally do the copy uv_other.data.foreach_set("uv_raw", uv_array) @@ -482,14 +590,18 @@ class MakeDupliFace(bpy.types.Operator): SCALE_FAC = 0.01 offset = 0.5 * SCALE_FAC - base_tri = Vector((-offset, -offset, 0.0)), Vector((offset, -offset, 0.0)), Vector((offset, offset, 0.0)), Vector((-offset, offset, 0.0)) + base_tri = (Vector((-offset, -offset, 0.0)), + Vector((+offset, -offset, 0.0)), + Vector((+offset, +offset, 0.0)), + Vector((-offset, +offset, 0.0)), + ) def matrix_to_quat(matrix): # scale = matrix.median_scale trans = matrix.to_translation() rot = matrix.to_3x3() # also contains scale - return [(b * rot) + trans for b in base_tri] + return [(rot * b) + trans for b in base_tri] scene = bpy.context.scene linked = {} for obj in bpy.context.selected_objects: @@ -498,7 +610,10 @@ class MakeDupliFace(bpy.types.Operator): linked.setdefault(data, []).append(obj) for data, objects in linked.items(): - face_verts = [axis for obj in objects for v in matrix_to_quat(obj.matrix_world) for axis in v] + face_verts = [axis for obj in objects + for v in matrix_to_quat(obj.matrix_world) + for axis in v] + faces = list(range(len(face_verts) // 3)) mesh = bpy.data.meshes.new(data.name + "_dupli") @@ -535,7 +650,8 @@ class MakeDupliFace(bpy.types.Operator): class IsolateTypeRender(bpy.types.Operator): - '''Hide unselected render objects of same type as active by setting the hide render flag''' + '''Hide unselected render objects of same type as active ''' \ + '''by setting the hide render flag''' bl_idname = "object.isolate_type_render" bl_label = "Restrict Render Unselected" bl_options = {'REGISTER', 'UNDO'} diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py index 644f30a4745..7fd769c40c9 100644 --- a/release/scripts/startup/bl_operators/object_align.py +++ b/release/scripts/startup/bl_operators/object_align.py @@ -16,106 +16,212 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy from mathutils import Vector -def align_objects(align_x, align_y, align_z, align_mode, relative_to): +def GlobalBB_LQ(bb_world): + + # Initialize the variables with the 8th vertex + left, right, front, back, down, up = (bb_world[7][0], + bb_world[7][0], + bb_world[7][1], + bb_world[7][1], + bb_world[7][2], + bb_world[7][2], + ) + + # Test against the other 7 verts + for i in range(7): + + # X Range + val = bb_world[i][0] + if val < left: + left = val + + if val > right: + right = val + + # Y Range + val = bb_world[i][1] + if val < front: + front = val + + if val > back: + back = val + + # Z Range + val = bb_world[i][2] + if val < down: + down = val + + if val > up: + up = val + + return (Vector((left, front, up)), Vector((right, back, down))) + + +def GlobalBB_HQ(obj): + + matrix_world = obj.matrix_world.copy() + + # Initialize the variables with the last vertex + + verts = obj.data.vertices + + val = matrix_world * verts[-1].co + + left, right, front, back, down, up = (val[0], + val[0], + val[1], + val[1], + val[2], + val[2], + ) + + # Test against all other verts + for i in range(len(verts) - 1): + + vco = matrix_world * verts[i].co + + # X Range + val = vco[0] + if val < left: + left = val + + if val > right: + right = val + + # Y Range + val = vco[1] + if val < front: + front = val + + if val > back: + back = val + + # Z Range + val = vco[2] + if val < down: + down = val + + if val > up: + up = val + + return Vector((left, front, up)), Vector((right, back, down)) + + +def align_objects(align_x, + align_y, + align_z, + align_mode, + relative_to, + bb_quality): cursor = bpy.context.scene.cursor_location - Left_Up_Front_SEL = [0.0, 0.0, 0.0] - Right_Down_Back_SEL = [0.0, 0.0, 0.0] + Left_Front_Up_SEL = [0.0, 0.0, 0.0] + Right_Back_Down_SEL = [0.0, 0.0, 0.0] flag_first = True objs = [] for obj in bpy.context.selected_objects: - matrix_world = obj.matrix_world - bb_world = [Vector(v[:]) * matrix_world for v in obj.bound_box] + matrix_world = obj.matrix_world.copy() + bb_world = [matrix_world * Vector(v[:]) for v in obj.bound_box] objs.append((obj, bb_world)) if not objs: return False for obj, bb_world in objs: - Left_Up_Front = bb_world[1] - Right_Down_Back = bb_world[7] + + if bb_quality: + GBB = GlobalBB_HQ(obj) + else: + GBB = GlobalBB_LQ(bb_world) + + Left_Front_Up = GBB[0] + Right_Back_Down = GBB[1] # Active Center if obj == bpy.context.active_object: - center_active_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0 - center_active_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0 - center_active_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0 + center_active_x = (Left_Front_Up[0] + Right_Back_Down[0]) / 2.0 + center_active_y = (Left_Front_Up[1] + Right_Back_Down[1]) / 2.0 + center_active_z = (Left_Front_Up[2] + Right_Back_Down[2]) / 2.0 - size_active_x = (Right_Down_Back[0] - Left_Up_Front[0]) / 2.0 - size_active_y = (Right_Down_Back[1] - Left_Up_Front[1]) / 2.0 - size_active_z = (Left_Up_Front[2] - Right_Down_Back[2]) / 2.0 + size_active_x = (Right_Back_Down[0] - Left_Front_Up[0]) / 2.0 + size_active_y = (Right_Back_Down[1] - Left_Front_Up[1]) / 2.0 + size_active_z = (Left_Front_Up[2] - Right_Back_Down[2]) / 2.0 # Selection Center if flag_first: flag_first = False - Left_Up_Front_SEL[0] = Left_Up_Front[0] - Left_Up_Front_SEL[1] = Left_Up_Front[1] - Left_Up_Front_SEL[2] = Left_Up_Front[2] + Left_Front_Up_SEL[0] = Left_Front_Up[0] + Left_Front_Up_SEL[1] = Left_Front_Up[1] + Left_Front_Up_SEL[2] = Left_Front_Up[2] - Right_Down_Back_SEL[0] = Right_Down_Back[0] - Right_Down_Back_SEL[1] = Right_Down_Back[1] - Right_Down_Back_SEL[2] = Right_Down_Back[2] + Right_Back_Down_SEL[0] = Right_Back_Down[0] + Right_Back_Down_SEL[1] = Right_Back_Down[1] + Right_Back_Down_SEL[2] = Right_Back_Down[2] else: # X axis - if Left_Up_Front[0] < Left_Up_Front_SEL[0]: - Left_Up_Front_SEL[0] = Left_Up_Front[0] + if Left_Front_Up[0] < Left_Front_Up_SEL[0]: + Left_Front_Up_SEL[0] = Left_Front_Up[0] # Y axis - if Left_Up_Front[1] < Left_Up_Front_SEL[1]: - Left_Up_Front_SEL[1] = Left_Up_Front[1] + if Left_Front_Up[1] < Left_Front_Up_SEL[1]: + Left_Front_Up_SEL[1] = Left_Front_Up[1] # Z axis - if Left_Up_Front[2] > Left_Up_Front_SEL[2]: - Left_Up_Front_SEL[2] = Left_Up_Front[2] + if Left_Front_Up[2] > Left_Front_Up_SEL[2]: + Left_Front_Up_SEL[2] = Left_Front_Up[2] # X axis - if Right_Down_Back[0] > Right_Down_Back_SEL[0]: - Right_Down_Back_SEL[0] = Right_Down_Back[0] + if Right_Back_Down[0] > Right_Back_Down_SEL[0]: + Right_Back_Down_SEL[0] = Right_Back_Down[0] # Y axis - if Right_Down_Back[1] > Right_Down_Back_SEL[1]: - Right_Down_Back_SEL[1] = Right_Down_Back[1] + if Right_Back_Down[1] > Right_Back_Down_SEL[1]: + Right_Back_Down_SEL[1] = Right_Back_Down[1] # Z axis - if Right_Down_Back[2] < Right_Down_Back_SEL[2]: - Right_Down_Back_SEL[2] = Right_Down_Back[2] + if Right_Back_Down[2] < Right_Back_Down_SEL[2]: + Right_Back_Down_SEL[2] = Right_Back_Down[2] - center_sel_x = (Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0]) / 2.0 - center_sel_y = (Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1]) / 2.0 - center_sel_z = (Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2]) / 2.0 + center_sel_x = (Left_Front_Up_SEL[0] + Right_Back_Down_SEL[0]) / 2.0 + center_sel_y = (Left_Front_Up_SEL[1] + Right_Back_Down_SEL[1]) / 2.0 + center_sel_z = (Left_Front_Up_SEL[2] + Right_Back_Down_SEL[2]) / 2.0 # Main Loop for obj, bb_world in objs: + matrix_world = obj.matrix_world.copy() + bb_world = [matrix_world * Vector(v[:]) for v in obj.bound_box] - loc_world = obj.location - bb_world = [Vector(v[:]) * obj.matrix_world for v in obj.bound_box] + if bb_quality: + GBB = GlobalBB_HQ(obj) + else: + GBB = GlobalBB_LQ(bb_world) - Left_Up_Front = bb_world[1] - Right_Down_Back = bb_world[7] + Left_Front_Up = GBB[0] + Right_Back_Down = GBB[1] - center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0 - center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0 - center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0 + center_x = (Left_Front_Up[0] + Right_Back_Down[0]) / 2.0 + center_y = (Left_Front_Up[1] + Right_Back_Down[1]) / 2.0 + center_z = (Left_Front_Up[2] + Right_Back_Down[2]) / 2.0 - positive_x = Right_Down_Back[0] - positive_y = Right_Down_Back[1] - positive_z = Left_Up_Front[2] + positive_x = Right_Back_Down[0] + positive_y = Right_Back_Down[1] + positive_z = Left_Front_Up[2] - negative_x = Left_Up_Front[0] - negative_y = Left_Up_Front[1] - negative_z = Right_Down_Back[2] + negative_x = Left_Front_Up[0] + negative_y = Left_Front_Up[1] + negative_z = Right_Back_Down[2] obj_loc = obj.location @@ -230,7 +336,7 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to): return True -from bpy.props import EnumProperty +from bpy.props import EnumProperty, BoolProperty class AlignObjects(bpy.types.Operator): @@ -239,6 +345,13 @@ class AlignObjects(bpy.types.Operator): bl_label = "Align Objects" bl_options = {'REGISTER', 'UNDO'} + bb_quality = BoolProperty( + name="High Quality", + description=("Enables high quality calculation of the " + "bounding box for perfect results on complex " + "shape meshes with rotation/scale (Slow)"), + default=True) + align_mode = EnumProperty(items=( ('OPT_1', "Negative Sides", ""), ('OPT_2', "Centers", ""), @@ -271,7 +384,12 @@ class AlignObjects(bpy.types.Operator): def execute(self, context): align_axis = self.align_axis - ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to) + ret = align_objects('X' in align_axis, + 'Y' in align_axis, + 'Z' in align_axis, + self.align_mode, + self.relative_to, + self.bb_quality) if not ret: self.report({'WARNING'}, "No objects with bound-box selected") diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 21640fa3ee6..ef10bfd737d 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -16,16 +16,38 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> from mathutils import Vector import bpy -from bpy.props import BoolProperty, EnumProperty, IntProperty, FloatProperty, FloatVectorProperty +from bpy.props import (BoolProperty, + EnumProperty, + IntProperty, + FloatProperty, + FloatVectorProperty, + ) + + +def object_ensure_material(obj, mat_name): + """ Use an existing material or add a new one. + """ + mat = mat_slot = None + for mat_slot in obj.material_slots: + mat = mat_slot.material + if mat: + break + if mat is None: + mat = bpy.data.materials.new(mat_name) + if mat_slot: + mat_slot.material = mat + else: + obj.data.materials.append(mat) + return mat -class MakeFur(bpy.types.Operator): - bl_idname = "object.make_fur" - bl_label = "Make Fur" +class QuickFur(bpy.types.Operator): + bl_idname = "object.quick_fur" + bl_label = "Quick Fur" bl_options = {'REGISTER', 'UNDO'} density = EnumProperty(items=( @@ -44,7 +66,8 @@ class MakeFur(bpy.types.Operator): def execute(self, context): fake_context = bpy.context.copy() - mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH'] + mesh_objects = [obj for obj in context.selected_objects + if obj.type == 'MESH'] if not mesh_objects: self.report({'ERROR'}, "Select at least one mesh object.") @@ -75,14 +98,164 @@ class MakeFur(bpy.types.Operator): psys.settings.child_type = 'INTERPOLATED' obj.data.materials.append(mat) - obj.particle_systems[-1].settings.material = len(obj.data.materials) + obj.particle_systems[-1].settings.material = \ + len(obj.data.materials) return {'FINISHED'} +class QuickExplode(bpy.types.Operator): + bl_idname = "object.quick_explode" + bl_label = "Quick Explode" + bl_options = {'REGISTER', 'UNDO'} + + style = EnumProperty(items=( + ('EXPLODE', "Explode", ""), + ('BLEND', "Blend", "")), + name="Explode Style", + description="", + default='EXPLODE') + + amount = IntProperty(name="Amount of pieces", + default=100, min=2, max=10000, soft_min=2, soft_max=10000) + + frame_duration = IntProperty(name="Duration", + default=50, min=1, max=300000, soft_min=1, soft_max=10000) + + frame_start = IntProperty(name="Start Frame", + default=1, min=1, max=300000, soft_min=1, soft_max=10000) + + frame_end = IntProperty(name="End Frame", + default=10, min=1, max=300000, soft_min=1, soft_max=10000) + + velocity = FloatProperty(name="Outwards Velocity", + default=1, min=0, max=300000, soft_min=0, soft_max=10) + + fade = BoolProperty(name="Fade", + description="Fade the pieces over time.", + default=True) + + def execute(self, context): + fake_context = bpy.context.copy() + obj_act = context.active_object + + if obj_act is None or obj_act.type != 'MESH': + self.report({'ERROR'}, "Active object is not a mesh") + return {'CANCELLED'} + + mesh_objects = [obj for obj in context.selected_objects + if obj.type == 'MESH' and obj != obj_act] + mesh_objects.insert(0, obj_act) + + if self.style == 'BLEND' and len(mesh_objects) != 2: + self.report({'ERROR'}, "Select two mesh objects") + return {'CANCELLED'} + elif not mesh_objects: + self.report({'ERROR'}, "Select at least one mesh object") + return {'CANCELLED'} + + for obj in mesh_objects: + if obj.particle_systems: + self.report({'ERROR'}, + "Object %r already has a " + "particle system" % obj.name) + + return {'CANCELLED'} + + if self.fade: + tex = bpy.data.textures.new("Explode fade", 'BLEND') + tex.use_color_ramp = True + + if self.style == 'BLEND': + tex.color_ramp.elements[0].position = 0.333 + tex.color_ramp.elements[1].position = 0.666 + + tex.color_ramp.elements[0].color[3] = 1.0 + tex.color_ramp.elements[1].color[3] = 0.0 + + if self.style == 'BLEND': + from_obj = mesh_objects[1] + to_obj = mesh_objects[0] + + for obj in mesh_objects: + fake_context["object"] = obj + bpy.ops.object.particle_system_add(fake_context) + + settings = obj.particle_systems[-1].settings + settings.count = self.amount + settings.frame_start = self.frame_start + settings.frame_end = self.frame_end - self.frame_duration + settings.lifetime = self.frame_duration + settings.normal_factor = self.velocity + settings.render_type = 'NONE' + + explode = obj.modifiers.new(name='Explode', type='EXPLODE') + explode.use_edge_cut = True + + if self.fade: + explode.show_dead = False + uv = obj.data.uv_textures.new("Explode fade") + explode.particle_uv = uv.name + + mat = object_ensure_material(obj, "Explode Fade") + + mat.use_transparency = True + mat.use_transparent_shadows = True + mat.alpha = 0.0 + mat.specular_alpha = 0.0 + + tex_slot = mat.texture_slots.add() + + tex_slot.texture = tex + tex_slot.texture_coords = 'UV' + tex_slot.uv_layer = uv.name + + tex_slot.use_map_alpha = True + + if self.style == 'BLEND': + if obj == to_obj: + tex_slot.alpha_factor = -1.0 + elem = tex.color_ramp.elements[1] + elem.color = mat.diffuse_color + else: + elem = tex.color_ramp.elements[0] + elem.color = mat.diffuse_color + else: + tex_slot.use_map_color_diffuse = False + + if self.style == 'BLEND': + settings.physics_type = 'KEYED' + settings.use_emit_random = False + settings.rotation_mode = 'NOR' + + psys = obj.particle_systems[-1] + + fake_context["particle_system"] = obj.particle_systems[-1] + bpy.ops.particle.new_target(fake_context) + bpy.ops.particle.new_target(fake_context) + + if obj == from_obj: + psys.targets[1].object = to_obj + else: + psys.targets[0].object = from_obj + settings.normal_factor = -self.velocity + explode.show_unborn = False + explode.show_dead = True + else: + settings.factor_random = self.velocity + settings.angular_velocity_factor = self.velocity / 10.0 + + return {'FINISHED'} + + def invoke(self, context, event): + self.frame_start = context.scene.frame_current + self.frame_end = self.frame_start + self.frame_duration + return self.execute(context) + + def obj_bb_minmax(obj, min_co, max_co): for i in range(0, 8): - bb_vec = Vector((obj.bound_box[i][0], obj.bound_box[i][1], obj.bound_box[i][2])) * obj.matrix_world + bb_vec = obj.matrix_world * Vector(obj.bound_box[i]) min_co[0] = min(bb_vec[0], min_co[0]) min_co[1] = min(bb_vec[1], min_co[1]) @@ -92,28 +265,33 @@ def obj_bb_minmax(obj, min_co, max_co): max_co[2] = max(bb_vec[2], max_co[2]) -class MakeSmoke(bpy.types.Operator): - bl_idname = "object.make_smoke" - bl_label = "Make Smoke" +class QuickSmoke(bpy.types.Operator): + bl_idname = "object.quick_smoke" + bl_label = "Quick Smoke" bl_options = {'REGISTER', 'UNDO'} - style = EnumProperty(items=( - ('STREAM', "Stream", ""), - ('PUFF', "Puff", ""), - ('FIRE', "Fire", "")), - name="Smoke Style", - description="", - default='STREAM') - - show_flows = BoolProperty(name="Render Smoke Objects", - description="Keep the smoke objects visible during rendering.", - default=False) + style = EnumProperty( + items=(('STREAM', "Stream", ""), + ('PUFF', "Puff", ""), + ('FIRE', "Fire", ""), + ), + name="Smoke Style", + description="", + default='STREAM', + ) + + show_flows = BoolProperty( + name="Render Smoke Objects", + description="Keep the smoke objects visible during rendering.", + default=False, + ) def execute(self, context): fake_context = bpy.context.copy() - mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH'] - min_co = Vector((100000, 100000, 100000)) - max_co = Vector((-100000, -100000, -100000)) + mesh_objects = [obj for obj in context.selected_objects + if obj.type == 'MESH'] + min_co = Vector((100000.0, 100000.0, 100000.0)) + max_co = -min_co if not mesh_objects: self.report({'ERROR'}, "Select at least one mesh object.") @@ -171,21 +349,25 @@ class MakeSmoke(bpy.types.Operator): mat.volume.density = 0 mat.volume.density_scale = 5 - mat.texture_slots.add() - mat.texture_slots[0].texture = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA') - mat.texture_slots[0].texture.voxel_data.domain_object = obj - mat.texture_slots[0].use_map_color_emission = False - mat.texture_slots[0].use_map_density = True + tex = bpy.data.textures.new("Smoke Density", 'VOXEL_DATA') + tex.voxel_data.domain_object = obj + + tex_slot = mat.texture_slots.add() + tex_slot.texture = tex + tex_slot.use_map_color_emission = False + tex_slot.use_map_density = True # for fire add a second texture for emission and emission color if self.style == 'FIRE': mat.volume.emission = 5 - mat.texture_slots.add() - mat.texture_slots[1].texture = bpy.data.textures.new("Smoke Heat", 'VOXEL_DATA') - mat.texture_slots[1].texture.voxel_data.domain_object = obj - mat.texture_slots[1].texture.use_color_ramp = True + tex = bpy.data.textures.new("Smoke Heat", 'VOXEL_DATA') + tex.voxel_data.domain_object = obj + tex.use_color_ramp = True + + tex_slot = mat.texture_slots.add() + tex_slot.texture = tex - ramp = mat.texture_slots[1].texture.color_ramp + ramp = tex.color_ramp elem = ramp.elements.new(0.333) elem.color[0] = elem.color[3] = 1 @@ -201,33 +383,43 @@ class MakeSmoke(bpy.types.Operator): return {'FINISHED'} -class MakeFluid(bpy.types.Operator): - bl_idname = "object.make_fluid" - bl_label = "Make Fluid" +class QuickFluid(bpy.types.Operator): + bl_idname = "object.quick_fluid" + bl_label = "Quick Fluid" bl_options = {'REGISTER', 'UNDO'} - style = EnumProperty(items=( - ('INFLOW', "Inflow", ""), - ('BASIC', "Basic", "")), + style = EnumProperty( + items=(('INFLOW', "Inflow", ""), + ('BASIC', "Basic", ""), + ), name="Fluid Style", description="", - default='BASIC') - - initial_velocity = FloatVectorProperty(name="Initial Velocity", - description="Initial velocity of the fluid", - default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='VELOCITY') - - show_flows = BoolProperty(name="Render Fluid Objects", - description="Keep the fluid objects visible during rendering.", - default=False) - - start_baking = BoolProperty(name="Start Fluid Bake", - description="Start baking the fluid immediately after creating the domain object.", - default=False) + default='BASIC', + ) + initial_velocity = FloatVectorProperty( + name="Initial Velocity", + description="Initial velocity of the fluid", + default=(0.0, 0.0, 0.0), + min=-100.0, + max=100.0, + subtype='VELOCITY', + ) + show_flows = BoolProperty( + name="Render Fluid Objects", + description="Keep the fluid objects visible during rendering.", + default=False, + ) + start_baking = BoolProperty( + name="Start Fluid Bake", + description=("Start baking the fluid immediately " + "after creating the domain object"), + default=False, + ) def execute(self, context): fake_context = bpy.context.copy() - mesh_objects = [obj for obj in context.selected_objects if (obj.type == 'MESH' and not 0 in obj.dimensions)] + mesh_objects = [obj for obj in context.selected_objects + if (obj.type == 'MESH' and not 0.0 in obj.dimensions)] min_co = Vector((100000, 100000, 100000)) max_co = Vector((-100000, -100000, -100000)) @@ -240,7 +432,8 @@ class MakeFluid(bpy.types.Operator): # make each selected object a fluid bpy.ops.object.modifier_add(fake_context, type='FLUID_SIMULATION') - # fluid has to be before constructive modifiers, so it might not be the last modifier + # fluid has to be before constructive modifiers, + # so it might not be the last modifier for mod in obj.modifiers: if mod.type == 'FLUID_SIMULATION': break @@ -264,10 +457,14 @@ class MakeFluid(bpy.types.Operator): obj = context.active_object obj.name = "Fluid Domain" - # give the fluid some room below the flows and scale with initial velocity + # give the fluid some room below the flows + # and scale with initial velocity v = 0.5 * self.initial_velocity obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, -1.0)) + v - obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0)) + Vector((abs(v[0]), abs(v[1]), abs(v[2]))) + obj.scale = (0.5 * (max_co - min_co) + + Vector((1.0, 1.0, 2.0)) + + Vector((abs(v[0]), abs(v[1]), abs(v[2]))) + ) # setup smoke domain bpy.ops.object.modifier_add(type='FLUID_SIMULATION') diff --git a/release/scripts/startup/bl_operators/object_randomize_transform.py b/release/scripts/startup/bl_operators/object_randomize_transform.py index 9dc5086086f..b94c4f06cd3 100644 --- a/release/scripts/startup/bl_operators/object_randomize_transform.py +++ b/release/scripts/startup/bl_operators/object_randomize_transform.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy @@ -93,40 +93,69 @@ class RandomizeLocRotSize(bpy.types.Operator): bl_label = "Randomize Transform" bl_options = {'REGISTER', 'UNDO'} - random_seed = IntProperty(name="Random Seed", - description="Seed value for the random generator", - default=0, min=0, max=1000) - - use_delta = BoolProperty(name="Transform Delta", - description="Randomize delta transform values instead of regular transform", default=False) - - use_loc = BoolProperty(name="Randomize Location", - description="Randomize the location values", default=True) - - loc = FloatVectorProperty(name="Location", - description="Maximun distance the objects can spread over each axis", - default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='TRANSLATION') - - use_rot = BoolProperty(name="Randomize Rotation", - description="Randomize the rotation values", default=True) - - rot = FloatVectorProperty(name="Rotation", - description="Maximun rotation over each axis", - default=(0.0, 0.0, 0.0), min=-180.0, max=180.0, subtype='TRANSLATION') - - use_scale = BoolProperty(name="Randomize Scale", - description="Randomize the scale values", default=True) - - scale_even = BoolProperty(name="Scale Even", - description="Use the same scale value for all axis", default=False) + random_seed = IntProperty( + name="Random Seed", + description="Seed value for the random generator", + min=0, + max=1000, + default=0, + ) + use_delta = BoolProperty( + name="Transform Delta", + description=("Randomize delta transform values " + "instead of regular transform"), + default=False, + ) + use_loc = BoolProperty( + name="Randomize Location", + description="Randomize the location values", + default=True, + ) + loc = FloatVectorProperty( + name="Location", + description=("Maximun distance the objects " + "can spread over each axis"), + min=-100.0, + max=100.0, + default=(0.0, 0.0, 0.0), + subtype='TRANSLATION', + ) + use_rot = BoolProperty( + name="Randomize Rotation", + description="Randomize the rotation values", + default=True, + ) + rot = FloatVectorProperty( + name="Rotation", + description="Maximun rotation over each axis", + min=-180.0, + max=180.0, + default=(0.0, 0.0, 0.0), + subtype='TRANSLATION', + ) + use_scale = BoolProperty( + name="Randomize Scale", + description="Randomize the scale values", + default=True, + ) + scale_even = BoolProperty( + name="Scale Even", + description="Use the same scale value for all axis", + default=False, + ) '''scale_min = FloatProperty(name="Minimun Scale Factor", description="Lowest scale percentage possible", default=0.15, min=-1.0, max=1.0, precision=3)''' - scale = FloatVectorProperty(name="Scale", - description="Maximum scale randomization over each axis", - default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='TRANSLATION') + scale = FloatVectorProperty( + name="Scale", + description="Maximum scale randomization over each axis", + min=-100.0, + max=100.0, + default=(0.0, 0.0, 0.0), + subtype='TRANSLATION', + ) def execute(self, context): from math import radians diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 493c51ad237..fbcc327c3bd 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy @@ -30,8 +30,15 @@ class AddPresetBase(): # bl_label = "Add a Python Preset" bl_options = {'REGISTER'} # only because invoke_props_popup requires. - name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen=64, default="") - remove_active = bpy.props.BoolProperty(default=False, options={'HIDDEN'}) + name = bpy.props.StringProperty( + name="Name", + description="Name of the preset, used to make the path name", + maxlen=64, + ) + remove_active = bpy.props.BoolProperty( + default=False, + options={'HIDDEN'}, + ) @staticmethod def as_filename(name): # could reuse for other presets @@ -54,7 +61,10 @@ class AddPresetBase(): filename = self.as_filename(name) - target_path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", self.preset_subdir), create=True) + target_path = os.path.join("presets", self.preset_subdir) + target_path = bpy.utils.user_resource('SCRIPTS', + target_path, + create=True) if not target_path: self.report({'WARNING'}, "Failed to create presets path") @@ -95,7 +105,9 @@ class AddPresetBase(): 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 = bpy.utils.preset_find(preset_active, + self.preset_subdir, + display_name=True) if not filepath: return {'CANCELLED'} @@ -133,8 +145,15 @@ class ExecutePreset(bpy.types.Operator): bl_idname = "script.execute_preset" bl_label = "Execute a Python Preset" - filepath = bpy.props.StringProperty(name="Path", description="Path of the Python file to execute", maxlen=512, default="") - menu_idname = bpy.props.StringProperty(name="Menu ID Name", description="ID name of the menu this was called from", default="") + filepath = bpy.props.StringProperty( + name="Path", + description="Path of the Python file to execute", + maxlen=512, + ) + menu_idname = bpy.props.StringProperty( + name="Menu ID Name", + description="ID name of the menu this was called from", + ) def execute(self, context): from os.path import basename @@ -182,7 +201,10 @@ class AddPresetSSS(AddPresetBase, bpy.types.Operator): preset_menu = "MATERIAL_MT_sss_presets" preset_defines = [ - "material = (bpy.context.material.active_node_material if bpy.context.material.active_node_material else bpy.context.material)" + ("material = " + "bpy.context.material.active_node_material " + "if bpy.context.material.active_node_material " + "else bpy.context.material") ] preset_values = [ @@ -306,7 +328,11 @@ class AddPresetOperator(AddPresetBase, bpy.types.Operator): bl_label = "Operator Preset" preset_menu = "WM_MT_operator_presets" - operator = bpy.props.StringProperty(name="Operator", maxlen=64, options={'HIDDEN'}) + operator = bpy.props.StringProperty( + name="Operator", + maxlen=64, + options={'HIDDEN'}, + ) # XXX, not ideal preset_defines = [ @@ -315,19 +341,22 @@ class AddPresetOperator(AddPresetBase, bpy.types.Operator): @property def preset_subdir(self): - return __class__.operator_path(self.operator) + return AddPresetOperator.operator_path(self.operator) @property def preset_values(self): properties_blacklist = bpy.types.Operator.bl_rna.properties.keys() prefix, suffix = self.operator.split("_OT_", 1) - operator_rna = getattr(getattr(bpy.ops, prefix.lower()), suffix).get_rna().bl_rna + op = getattr(getattr(bpy.ops, prefix.lower()), suffix) + operator_rna = op.get_rna().bl_rna + del op ret = [] for prop_id, prop in operator_rna.properties.items(): - if (not prop.is_hidden) and prop_id not in properties_blacklist: - ret.append("op.%s" % prop_id) + if not (prop.is_hidden or prop.is_skip_save): + if prop_id not in properties_blacklist: + ret.append("op.%s" % prop_id) return ret diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py index 64af25e7b0f..a38d817d738 100644 --- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py +++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py @@ -1,27 +1,23 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** +# ##### BEGIN GPL LICENSE BLOCK ##### # -# Script copyright (C) Campbell J Barton +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ***** END GPL LICENCE BLOCK ***** +# ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> -# History -# # Originally written by Matt Ebb import bpy @@ -46,8 +42,10 @@ def guess_player_path(preset): player_path = "djv_view" if sys.platform == "darwin": - # TODO, crummy supporting only 1 version, could find the newest installed version - test_path = '/Applications/djv-0.8.2.app/Contents/Resources/bin/djv_view' + # TODO, crummy supporting only 1 version, + # could find the newest installed version + test_path = ("/Applications/djv-0.8.2.app" + "/Contents/Resources/bin/djv_view") if os.path.exists(test_path): player_path = test_path @@ -60,6 +58,9 @@ def guess_player_path(preset): elif preset == 'MPLAYER': player_path = "mplayer" + else: + player_path = "" + return player_path @@ -78,14 +79,14 @@ class PlayRenderedAnim(bpy.types.Operator): preset = prefs.filepaths.animation_player_preset player_path = prefs.filepaths.animation_player - file_path = bpy.path.abspath(rd.filepath) + # file_path = bpy.path.abspath(rd.filepath) # UNUSED is_movie = rd.is_movie_format # try and guess a command line if it doesn't exist - if player_path == '': + if player_path == "": player_path = guess_player_path(preset) - if is_movie == False and preset in ('FRAMECYCLER', 'RV', 'MPLAYER'): + if is_movie == False and preset in {'FRAMECYCLER', 'RV', 'MPLAYER'}: # replace the number with '#' file_a = rd.frame_path(frame=0) @@ -95,11 +96,11 @@ class PlayRenderedAnim(bpy.types.Operator): while len(file_a) == len(file_b): frame_tmp = (frame_tmp * 10) + 9 - print(frame_tmp) file_b = rd.frame_path(frame=frame_tmp) file_b = rd.frame_path(frame=int(frame_tmp / 10)) - file = "".join((c if file_b[i] == c else "#") for i, c in enumerate(file_a)) + file = ("".join((c if file_b[i] == c else "#") + for i, c in enumerate(file_a))) else: # works for movies and images file = rd.frame_path(frame=scene.frame_start) @@ -109,10 +110,35 @@ class PlayRenderedAnim(bpy.types.Operator): cmd = [player_path] # extra options, fps controls etc. if preset == 'BLENDER24': + # ----------------------------------------------------------------- + # Check blender is not 2.5x until it supports playback again + try: + process = subprocess.Popen([player_path, '--version'], + stdout=subprocess.PIPE, + ) + except: + # ignore and allow the main execution to catch the problem. + process = None + + if process is not None: + process.wait() + out = process.stdout.read() + process.stdout.close() + out_split = out.strip().split() + if out_split[0] == b'Blender': + if not out_split[1].startswith(b'2.4'): + self.report({'ERROR'}, + "Blender %s doesn't support playback: %r" % + (out_split[1].decode(), player_path)) + return {'CANCELLED'} + del out, out_split + del process + # ----------------------------------------------------------------- + opts = ["-a", "-f", str(rd.fps), str(rd.fps_base), file] cmd.extend(opts) elif preset == 'DJV': - opts = [file, "-playback_speed", str(rd.fps)] + opts = [file, "-playback_speed", "%d" % int(rd.fps / rd.fps_base)] cmd.extend(opts) elif preset == 'FRAMECYCLER': opts = [file, "%d-%d" % (scene.frame_start, scene.frame_end)] @@ -125,18 +151,26 @@ class PlayRenderedAnim(bpy.types.Operator): if is_movie: opts.append(file) else: - opts.append("mf://%s" % file.replace("#", "?")) - opts += ["-mf", "fps=%.4f" % (rd.fps / rd.fps_base)] + opts += [("mf://%s" % file.replace("#", "?")), + "-mf", + "fps=%.4f" % (rd.fps / rd.fps_base), + ] + opts += ["-loop", "0", "-really-quiet", "-fs"] cmd.extend(opts) else: # 'CUSTOM' cmd.append(file) # launch it + print("Executing command:\n %r" % " ".join(cmd)) + try: process = subprocess.Popen(cmd) - except: - pass - #raise OSError("Couldn't find an external animation player.") + except Exception as e: + import traceback + self.report({'ERROR'}, + "Couldn't run external animation player with command " + "%r\n%s" % (" ".join(cmd), str(e))) + return {'CANCELLED'} return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py index ad5ec15ff80..43ca9af59ba 100644 --- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py +++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py @@ -25,6 +25,8 @@ import bpy def extend(obj, operator, EXTEND_MODE): + from bpy_extras import mesh_utils + me = obj.data me_verts = me.vertices # script will fail without UVs @@ -40,7 +42,6 @@ def extend(obj, operator, EXTEND_MODE): edge_average_lengths = {} OTHER_INDEX = 2, 3, 0, 1 - FAST_INDICIES = 0, 2, 1, 3 # order is faster def extend_uvs(face_source, face_target, edge_key): ''' @@ -170,7 +171,7 @@ def extend(obj, operator, EXTEND_MODE): edge_faces[edkey] = [i] if EXTEND_MODE == 'LENGTH': - edge_loops = me.edge_loops_from_faces(face_sel, [ed.key for ed in me.edges if ed.use_seam]) + edge_loops = mesh_utils.edge_loops_from_faces(me, face_sel, [ed.key for ed in me.edges if ed.use_seam]) me_verts = me.vertices for loop in edge_loops: looplen = [0.0] diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 3893612437a..9ae0cd0ddf9 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -406,7 +406,7 @@ def lightmap_uvpack(meshes, ok = False # Tall boxes in groups of 2 - for d, boxes in odd_dict.items(): + for d, boxes in list(odd_dict.items()): if d[1] < max_int_dimension: #\boxes.sort(key = lambda a: len(a.children)) while len(boxes) >= 2: @@ -427,7 +427,7 @@ def lightmap_uvpack(meshes, odd_dict.setdefault((w, h), []).append(pf_parent) # Even boxes in groups of 4 - for d, boxes in even_dict.items(): + for d, boxes in list(even_dict.items()): if d < max_int_dimension: boxes.sort(key=lambda a: len(a.children)) @@ -444,7 +444,7 @@ def lightmap_uvpack(meshes, del even_dict del odd_dict - orig = len(pretty_faces) + # orig = len(pretty_faces) pretty_faces = [pf for pf in pretty_faces if not pf.has_parent] @@ -489,7 +489,10 @@ def lightmap_uvpack(meshes, if PREF_APPLY_IMAGE: if not PREF_PACK_IN_ONE: - image = Image.New("lightmap", PREF_IMG_PX_SIZE, PREF_IMG_PX_SIZE, 24) + image = bpy.data.images.new(name="lightmap", + width=PREF_IMG_PX_SIZE, + height=PREF_IMG_PX_SIZE, + ) for f in face_sel: # f.image = image @@ -530,7 +533,7 @@ def unwrap(operator, context, **kwargs): return {'FINISHED'} -from bpy.props import BoolProperty, FloatProperty, IntProperty, EnumProperty +from bpy.props import BoolProperty, FloatProperty, IntProperty class LightMapPack(bpy.types.Operator): diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py index 4f5b1d8b233..851f33bde11 100644 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# TODO <pep8 compliant> from mathutils import Matrix, Vector, geometry import bpy @@ -243,7 +243,7 @@ def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1): # Do this allong the way if mat != -1: - v = vecs[i] = v*mat + v = vecs[i] = mat * v x= v.x y= v.y if x<minx: minx= x @@ -746,13 +746,9 @@ def packIslands(islandList): uv.y= (uv.y+yoffset) * yfactor - def VectoQuat(vec): vec = vec.normalized() - if abs(vec.x) > 0.5: - return vec.to_track_quat('Z', 'X') - else: - return vec.to_track_quat('Z', 'Y') + return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted() class thickface(object): @@ -791,7 +787,11 @@ def main_consts(): global ob ob = None -def main(context, island_margin, projection_limit): +def main(context, + island_margin, + projection_limit, + user_area_weight, + ): global USER_FILL_HOLES global USER_FILL_HOLES_QUALITY global USER_STRETCH_ASPECT @@ -812,21 +812,25 @@ def main(context, island_margin, projection_limit): global RotMatStepRotation main_consts() -#XXX objects= bpy.data.scenes.active.objects - objects = context.selected_editable_objects - + # TODO, all selected meshes + ''' + # objects = context.selected_editable_objects + objects = [] # we can will tag them later. obList = [ob for ob in objects if ob.type == 'MESH'] # Face select object may not be selected. -#XXX ob = objects.active - ob= objects[0] + ob = context.active_object if ob and (not ob.select) and ob.type == 'MESH': # Add to the list obList =[ob] del objects + ''' + + # quick workaround + obList = [ob for ob in [context.active_object] if ob and ob.type == 'MESH'] if not obList: raise('error, no selected mesh objects') @@ -840,7 +844,6 @@ def main(context, island_margin, projection_limit): USER_FILL_HOLES = (0) USER_FILL_HOLES_QUALITY = (50) # Only for hole filling. USER_VIEW_INIT = (0) # Only for hole filling. - USER_AREA_WEIGHT = (1) # Only for hole filling. # Reuse variable if len(obList) == 1: @@ -966,12 +969,15 @@ def main(context, island_margin, projection_limit): # Add the average of all these faces normals as a projectionVec averageVec = Vector((0.0, 0.0, 0.0)) - if USER_AREA_WEIGHT: + if user_area_weight == 0.0: for fprop in newProjectMeshFaces: - averageVec += (fprop.no * fprop.area) + averageVec += fprop.no + elif user_area_weight == 1.0: + for fprop in newProjectMeshFaces: + averageVec += fprop.no * fprop.area else: for fprop in newProjectMeshFaces: - averageVec += fprop.no + averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight)) if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN projectVecs.append(averageVec.normalized()) @@ -1058,7 +1064,7 @@ def main(context, island_margin, projection_limit): f_uv = f.uv for j, v in enumerate(f.v): # XXX - note, between mathutils in 2.4 and 2.5 the order changed. - f_uv[j][:] = (v.co * MatQuat)[:2] + f_uv[j][:] = (MatQuat * v.co).xy if USER_SHARE_SPACE: @@ -1094,12 +1100,8 @@ def main(context, island_margin, projection_limit): """ pup_block = [\ 'Projection',\ -* ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, ''),\ ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\ ('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\ - ('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\ - '',\ - '',\ '',\ 'UV Layout',\ ('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\ @@ -1121,11 +1123,15 @@ class SmartProject(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} angle_limit = FloatProperty(name="Angle Limit", - description="lower for more projection groups, higher for less distortion.", + description="lower for more projection groups, higher for less distortion", default=66.0, min=1.0, max=89.0) island_margin = FloatProperty(name="Island Margin", - description="Margin to reduce bleed from adjacent islands.", + description="Margin to reduce bleed from adjacent islands", + default=0.0, min=0.0, max=1.0) + + user_area_weight = FloatProperty(name="Area Weight", + description="Weight projections vector by faces with larger areas", default=0.0, min=0.0, max=1.0) @classmethod @@ -1133,7 +1139,11 @@ class SmartProject(bpy.types.Operator): return context.active_object != None def execute(self, context): - main(context, self.island_margin, self.angle_limit) + main(context, + self.island_margin, + self.angle_limit, + self.user_area_weight, + ) return {'FINISHED'} def invoke(self, context, event): diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 53c8d562297..af33e45668c 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -19,7 +19,9 @@ # <pep8 compliant> import bpy -from bpy.props import StringProperty, BoolProperty, IntProperty, FloatProperty +from bpy.props import StringProperty, BoolProperty, IntProperty, \ + FloatProperty, EnumProperty + from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear @@ -106,7 +108,7 @@ class WM_OT_context_set_boolean(bpy.types.Operator): '''Set a context value.''' bl_idname = "wm.context_set_boolean" bl_label = "Context Set Boolean" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = BoolProperty(name="Value", @@ -119,7 +121,7 @@ class WM_OT_context_set_int(bpy.types.Operator): # same as enum '''Set a context value.''' bl_idname = "wm.context_set_int" bl_label = "Context Set" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = IntProperty(name="Value", description="Assign value", default=0) @@ -132,7 +134,7 @@ class WM_OT_context_scale_int(bpy.types.Operator): '''Scale an int context value.''' bl_idname = "wm.context_scale_int" bl_label = "Context Set" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = FloatProperty(name="Value", description="Assign value", default=1.0) @@ -168,7 +170,7 @@ class WM_OT_context_set_float(bpy.types.Operator): # same as enum '''Set a context value.''' bl_idname = "wm.context_set_float" bl_label = "Context Set Float" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = FloatProperty(name="Value", @@ -182,7 +184,7 @@ class WM_OT_context_set_string(bpy.types.Operator): # same as enum '''Set a context value.''' bl_idname = "wm.context_set_string" bl_label = "Context Set String" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = StringProperty(name="Value", @@ -195,7 +197,7 @@ class WM_OT_context_set_enum(bpy.types.Operator): '''Set a context value.''' bl_idname = "wm.context_set_enum" bl_label = "Context Set Enum" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = StringProperty(name="Value", @@ -209,7 +211,7 @@ class WM_OT_context_set_value(bpy.types.Operator): '''Set a context value.''' bl_idname = "wm.context_set_value" bl_label = "Context Set Value" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = StringProperty(name="Value", @@ -227,7 +229,7 @@ class WM_OT_context_toggle(bpy.types.Operator): '''Toggle a context value.''' bl_idname = "wm.context_toggle" bl_label = "Context Toggle" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop @@ -246,7 +248,7 @@ class WM_OT_context_toggle_enum(bpy.types.Operator): '''Toggle a context value.''' bl_idname = "wm.context_toggle_enum" bl_label = "Context Toggle Values" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value_1 = StringProperty(name="Value", \ @@ -273,7 +275,7 @@ class WM_OT_context_cycle_int(bpy.types.Operator): '''vertex keys, groups' etc.''' bl_idname = "wm.context_cycle_int" bl_label = "Context Int Cycle" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop reverse = rna_reverse_prop @@ -307,7 +309,7 @@ class WM_OT_context_cycle_enum(bpy.types.Operator): '''Toggle a context value.''' bl_idname = "wm.context_cycle_enum" bl_label = "Context Enum Cycle" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop reverse = rna_reverse_prop @@ -360,7 +362,7 @@ class WM_OT_context_cycle_array(bpy.types.Operator): Useful for cycling the active mesh edit mode.''' bl_idname = "wm.context_cycle_array" bl_label = "Context Array Cycle" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop reverse = rna_reverse_prop @@ -406,7 +408,7 @@ class WM_MT_context_menu_enum(bpy.types.Menu): class WM_OT_context_menu_enum(bpy.types.Operator): bl_idname = "wm.context_menu_enum" bl_label = "Context Enum Menu" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop def execute(self, context): @@ -420,7 +422,7 @@ class WM_OT_context_set_id(bpy.types.Operator): '''Toggle a context value.''' bl_idname = "wm.context_set_id" bl_label = "Set Library ID" - bl_options = {'UNDO'} + bl_options = {'UNDO', 'INTERNAL'} data_path = rna_path_prop value = StringProperty(name="Value", @@ -457,14 +459,76 @@ doc_id = StringProperty(name="Doc ID", doc_new = StringProperty(name="Edit Description", description="", maxlen=1024, default="") +data_path_iter = StringProperty( + description="The data path relative to the context, must point to an iterable.") + +data_path_item = StringProperty( + description="The data path from each iterable to the value (int or float)") + + +class WM_OT_context_collection_boolean_set(bpy.types.Operator): + '''Set boolean values for a collection of items''' + bl_idname = "wm.context_collection_boolean_set" + bl_label = "Context Collection Boolean Set" + bl_options = {'UNDO', 'REGISTER', 'INTERNAL'} + + data_path_iter = data_path_iter + data_path_item = data_path_item + + type = EnumProperty(items=( + ('TOGGLE', "Toggle", ""), + ('ENABLE', "Enable", ""), + ('DISABLE', "Disable", ""), + ), + name="Type") + + def execute(self, context): + data_path_iter = self.data_path_iter + data_path_item = self.data_path_item + + items = list(getattr(context, data_path_iter)) + items_ok = [] + is_set = False + for item in items: + try: + value_orig = eval("item." + data_path_item) + except: + continue + + if value_orig == True: + is_set = True + elif value_orig == False: + pass + else: + self.report({'WARNING'}, "Non boolean value found: %s[ ].%s" % + (data_path_iter, data_path_item)) + return {'CANCELLED'} + + items_ok.append(item) + + if self.type == 'ENABLE': + is_set = True + elif self.type == 'DISABLE': + is_set = False + else: + is_set = not is_set + + exec_str = "item.%s = %s" % (data_path_item, is_set) + for item in items_ok: + exec(exec_str) + + return {'FINISHED'} + class WM_OT_context_modal_mouse(bpy.types.Operator): '''Adjust arbitrary values with mouse input''' bl_idname = "wm.context_modal_mouse" bl_label = "Context Modal Mouse" + bl_options = {'GRAB_POINTER', 'BLOCKING', 'INTERNAL'} + + data_path_iter = data_path_iter + data_path_item = data_path_item - data_path_iter = StringProperty(description="The data path relative to the context, must point to an iterable.") - data_path_item = StringProperty(description="The data path from each iterable to the value (int or float)") input_scale = FloatProperty(default=0.01, description="Scale the mouse movement by this value before applying the delta") invert = BoolProperty(default=False, description="Invert the mouse input") initial_x = IntProperty(options={'HIDDEN'}) @@ -940,6 +1004,14 @@ class WM_OT_copy_prev_settings(bpy.types.Operator): self.report({'ERROR'}, "Source path %r exists" % path_src) else: shutil.copytree(path_src, path_dst) + + # in 2.57 and earlier windows installers, system scripts were copied + # into the configuration directory, don't want to copy those + system_script = os.path.join(path_dst, 'scripts/modules/bpy_types.py') + if os.path.isfile(system_script): + shutil.rmtree(os.path.join(path_dst, 'scripts')) + shutil.rmtree(os.path.join(path_dst, 'plugins')) + # dont loose users work if they open the splash later. if bpy.data.is_saved is bpy.data.is_dirty is False: bpy.ops.wm.read_homefile() diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 5c565fbd300..bf63c6071b9 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -85,25 +85,26 @@ def register(): from bpy.props import StringProperty, EnumProperty WindowManager = bpy.types.WindowManager + def addon_filter_items(self, context): + import addon_utils + + items = [('All', "All", ""), + ('Enabled', "Enabled", ""), + ('Disabled', "Disabled", ""), + ] + + items_unique = set() + + for mod in addon_utils.modules(space_userpref.USERPREF_PT_addons._addons_fake_modules): + info = addon_utils.module_bl_info(mod) + items_unique.add(info["category"]) + + items.extend([(cat, cat, "") for cat in sorted(items_unique)]) + return items + WindowManager.addon_search = StringProperty(name="Search", description="Search within the selected filter") WindowManager.addon_filter = EnumProperty( - items=[('All', "All", ""), - ('Enabled', "Enabled", ""), - ('Disabled', "Disabled", ""), - ('3D View', "3D View", ""), - ('Add Curve', "Add Curve", ""), - ('Add Mesh', "Add Mesh", ""), - ('Animation', "Animation", ""), - ('Development', "Development", ""), - ('Game Engine', "Game Engine", ""), - ('Import-Export', "Import-Export", ""), - ('Mesh', "Mesh", ""), - ('Object', "Object", ""), - ('Render', "Render", ""), - ('Rigging', "Rigging", ""), - ('Text Editor', "Text Editor", ""), - ('System', "System", "") - ], + items=addon_filter_items, name="Category", description="Filter add-ons by category", ) diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py index eb1bbfd2fb1..3b33a7ccc61 100644 --- a/release/scripts/startup/bl_ui/properties_animviz.py +++ b/release/scripts/startup/bl_ui/properties_animviz.py @@ -94,4 +94,5 @@ class OnionSkinButtonsPanel(): col.prop(arm, "show_only_ghost_selected", text="Selected Only") if __name__ == "__main__": # only for live edit. + import bpy bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 9477dc866ab..f2a3bac2373 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -299,10 +299,7 @@ class DATA_PT_onion_skinning(OnionSkinButtonsPanel): # , bpy.types.Panel): # in return (context.object) and (context.armature) def draw(self, context): - layout = self.layout - ob = context.object - self.draw_settings(context, ob.pose.animation_visualisation, bones=True) diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index e5076fd20d5..80cd5227fca 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -127,6 +127,7 @@ class DATA_PT_camera_display(CameraButtonsPanel, bpy.types.Panel): col.prop(cam, "show_mist", text="Mist") col.prop(cam, "show_title_safe", text="Title Safe") col.prop(cam, "show_name", text="Name") + col.prop_menu_enum(cam, "show_guide") col = split.column() col.prop(cam, "draw_size", text="Size") diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index 623daffac93..3c88127c724 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -113,9 +113,24 @@ class DATA_PT_shape_curve(CurveButtonsPanel, bpy.types.Panel): sub.prop(curve, "use_fill_back") col.prop(curve, "use_fill_deform", text="Fill Deformed") - col.label(text="Textures:") - col.prop(curve, "use_uv_as_generated") - col.prop(curve, "use_auto_texspace") + +class DATA_PT_curve_texture_space(CurveButtonsPanel, bpy.types.Panel): + bl_label = "Texture Space" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def draw(self, context): + layout = self.layout + + curve = context.curve + + row = layout.row() + row.prop(curve, "use_auto_texspace") + row.prop(curve, "use_uv_as_generated") + + row = layout.row() + row.column().prop(curve, "texspace_location", text="Location") + row.column().prop(curve, "texspace_size", text="Size") class DATA_PT_geometry_curve(CurveButtonsPanel, bpy.types.Panel): diff --git a/release/scripts/startup/bl_ui/properties_data_empty.py b/release/scripts/startup/bl_ui/properties_data_empty.py index e46cd1270ad..42b0af7eaf5 100644 --- a/release/scripts/startup/bl_ui/properties_data_empty.py +++ b/release/scripts/startup/bl_ui/properties_data_empty.py @@ -39,6 +39,15 @@ class DATA_PT_empty(DataButtonsPanel, bpy.types.Panel): ob = context.object layout.prop(ob, "empty_draw_type", text="Display") + + if ob.empty_draw_type == 'IMAGE': + layout.template_ID(ob, "data", open="image.open", unlink="image.unlink") + + layout.prop(ob, "color", text="Transparency", index=3, slider=True) + row = layout.row(align=True) + row.prop(ob, "empty_image_offset", text="Offset X", index=0) + row.prop(ob, "empty_image_offset", text="Offset Y", index=1) + layout.prop(ob, "empty_draw_size", text="Size") if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index b1d1789fadd..618a88f0879 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -46,6 +46,8 @@ class MESH_MT_shape_key_specials(bpy.types.Menu): layout.operator("object.shape_key_transfer", icon='COPY_ID') # icon is not ideal layout.operator("object.join_shapes", icon='COPY_ID') # icon is not ideal layout.operator("object.shape_key_mirror", icon='ARROW_LEFTRIGHT') + op = layout.operator("object.shape_key_add", icon='ZOOMIN', text="New Shape From Mix") + op.from_mix = True class MeshButtonsPanel(): @@ -97,8 +99,9 @@ class DATA_PT_normals(MeshButtonsPanel, bpy.types.Panel): split.prop(mesh, "show_double_sided") -class DATA_PT_settings(MeshButtonsPanel, bpy.types.Panel): - bl_label = "Settings" +class DATA_PT_texture_space(MeshButtonsPanel, bpy.types.Panel): + bl_label = "Texture Space" + bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): @@ -107,7 +110,13 @@ class DATA_PT_settings(MeshButtonsPanel, bpy.types.Panel): mesh = context.mesh layout.prop(mesh, "texture_mesh") + + layout.separator() + layout.prop(mesh, "use_auto_texspace") + row = layout.row() + row.column().prop(mesh, "texspace_location", text="Location") + row.column().prop(mesh, "texspace_size", text="Size") class DATA_PT_vertex_groups(MeshButtonsPanel, bpy.types.Panel): @@ -193,7 +202,8 @@ class DATA_PT_shape_keys(MeshButtonsPanel, bpy.types.Panel): col = row.column() sub = col.column(align=True) - sub.operator("object.shape_key_add", icon='ZOOMIN', text="") + op = sub.operator("object.shape_key_add", icon='ZOOMIN', text="") + op.from_mix = False sub.operator("object.shape_key_remove", icon='ZOOMOUT', text="") sub.menu("MESH_MT_shape_key_specials", icon='DOWNARROW_HLT', text="") @@ -280,9 +290,8 @@ class DATA_PT_texface(MeshButtonsPanel, bpy.types.Panel): @classmethod def poll(cls, context): - ob = context.active_object - - return (context.mode == 'EDIT_MESH') and ob and ob.type == 'MESH' + obj = context.object + return (context.mode == 'EDIT_MESH') and obj and obj.type == 'MESH' def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_data_metaball.py b/release/scripts/startup/bl_ui/properties_data_metaball.py index 81ba15d6f40..6dda99bc37f 100644 --- a/release/scripts/startup/bl_ui/properties_data_metaball.py +++ b/release/scripts/startup/bl_ui/properties_data_metaball.py @@ -72,6 +72,23 @@ class DATA_PT_metaball(DataButtonsPanel, bpy.types.Panel): layout.prop(mball, "update_method", expand=True) +class DATA_PT_mball_texture_space(DataButtonsPanel, bpy.types.Panel): + bl_label = "Texture Space" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def draw(self, context): + layout = self.layout + + mball = context.meta_ball + + layout.prop(mball, "use_auto_texspace") + + row = layout.row() + row.column().prop(mball, "texspace_location", text="Location") + row.column().prop(mball, "texspace_size", text="Size") + + class DATA_PT_metaball_element(DataButtonsPanel, bpy.types.Panel): bl_label = "Active Element" diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 27af84dbcfb..0a4d0b60514 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -219,7 +219,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel): col.label(text="Texture Coordinates:") col.prop(md, "texture_coords", text="") if md.texture_coords == 'OBJECT': - layout.prop(md, "texture_coordinate_object", text="Object") + layout.prop(md, "texture_coords_object", text="Object") elif md.texture_coords == 'UV' and ob.type == 'MESH': layout.prop_search(md, "uv_layer", ob.data, "uv_textures") @@ -394,6 +394,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel): col.operator("object.multires_higher_levels_delete", text="Delete Higher") col.operator("object.multires_reshape", text="Reshape") col.operator("object.multires_base_apply", text="Apply Base") + col.prop(md, "use_subsurf_uv") col.prop(md, "show_only_control_edges") layout.separator() @@ -483,11 +484,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel): col.label(text="Mode:") col.prop(md, "wrap_method", text="") - split = layout.split(percentage=0.25) - - col = split.column() - if md.wrap_method == 'PROJECT': + split = layout.split(percentage=0.25) + + col = split.column() col.label(text="Axis:") col.prop(md, "use_project_x") col.prop(md, "use_project_y") @@ -499,7 +499,6 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel): col.prop(md, "use_positive_direction") col = split.column() - col.label(text="Cull Faces:") col.prop(md, "cull_face", expand=True) @@ -673,7 +672,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel): col.prop(md, "texture_coords", text="") if md.texture_coords == 'OBJECT': - layout.prop(md, "texture_coordinate_object", text="Object") + layout.prop(md, "texture_coords_object", text="Object") elif md.texture_coords == 'UV' and ob.type == 'MESH': layout.prop_search(md, "uv_layer", ob.data, "uv_textures") diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 52d6b5f1376..2a52ae23782 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -174,6 +174,7 @@ class MATERIAL_PT_pipeline(MaterialButtonsPanel, bpy.types.Panel): row.prop(mat, "use_transparency") sub = row.column() sub.prop(mat, "offset_z") + sub.active = mat_type and mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY' row = layout.row() @@ -199,6 +200,7 @@ class MATERIAL_PT_pipeline(MaterialButtonsPanel, bpy.types.Panel): col.prop(mat, "shadow_cast_alpha", text="Casting Alpha") col.prop(mat, "use_cast_buffer_shadows") col.prop(mat, "use_cast_approximate") + col.prop(mat, "pass_index") class MATERIAL_PT_diffuse(MaterialButtonsPanel, bpy.types.Panel): @@ -729,6 +731,8 @@ class MATERIAL_PT_options(MaterialButtonsPanel, bpy.types.Panel): col.prop(mat, "use_vertex_color_paint") col.prop(mat, "use_vertex_color_light") col.prop(mat, "use_object_color") + if simple_material(base_mat): + col.prop(mat, "pass_index") class MATERIAL_PT_shadow(MaterialButtonsPanel, bpy.types.Panel): @@ -883,7 +887,7 @@ class MATERIAL_PT_volume_lighting(VolumeButtonsPanel, bpy.types.Panel): sub = col.column() sub.enabled = True sub.active = False - sub.prop(vol, "use_light_cache") + sub.label("Light Cache Enabled") col.prop(vol, "cache_resolution") sub = col.column(align=True) diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index ae66642e903..cdbcf2cf533 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -34,7 +34,6 @@ class OBJECT_PT_context_object(ObjectButtonsPanel, bpy.types.Panel): def draw(self, context): layout = self.layout space = context.space_data - ob = context.object if space.use_pin_id: layout.template_ID(space, "pin_id") diff --git a/release/scripts/startup/bl_ui/properties_object_constraint.py b/release/scripts/startup/bl_ui/properties_object_constraint.py index 59f9ca16d1a..5f79dd3127a 100644 --- a/release/scripts/startup/bl_ui/properties_object_constraint.py +++ b/release/scripts/startup/bl_ui/properties_object_constraint.py @@ -651,6 +651,24 @@ class ConstraintButtonsPanel(): sub.prop(con, "from_min_z", text="Min") sub.prop(con, "from_max_z", text="Max") + col = layout.column() + row = col.row() + row.label(text="Source to Destination Mapping:") + + # note: chr(187) is the ASCII arrow ( >> ). Blender Text Editor can't + # open it. Thus we are using the hardcoded value instead. + row = col.row() + row.prop(con, "map_to_x_from", expand=False, text="") + row.label(text=" %s X" % chr(187)) + + row = col.row() + row.prop(con, "map_to_y_from", expand=False, text="") + row.label(text=" %s Y" % chr(187)) + + row = col.row() + row.prop(con, "map_to_z_from", expand=False, text="") + row.label(text=" %s Z" % chr(187)) + split = layout.split() col = split.column() @@ -661,7 +679,6 @@ class ConstraintButtonsPanel(): col = split.column() col.label(text="X:") - col.row().prop(con, "map_to_x_from", expand=True) sub = col.column(align=True) sub.prop(con, "to_min_x", text="Min") @@ -669,7 +686,6 @@ class ConstraintButtonsPanel(): col = split.column() col.label(text="Y:") - col.row().prop(con, "map_to_y_from", expand=True) sub = col.column(align=True) sub.prop(con, "to_min_y", text="Min") @@ -677,7 +693,6 @@ class ConstraintButtonsPanel(): col = split.column() col.label(text="Z:") - col.row().prop(con, "map_to_z_from", expand=True) sub = col.column(align=True) sub.prop(con, "to_min_z", text="Min") diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 5037308c6fd..4c92296dacd 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -168,10 +168,8 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, bpy.types.Panel): if psys != None and psys.is_edited: if psys.is_global_hair: layout.operator("particle.connect_hair") - layout.label(text="Hair is disconnected.") else: layout.operator("particle.disconnect_hair") - layout.label(text="") elif psys != None and part.type == 'REACTOR': split.enabled = particle_panel_enabled(context, psys) split.prop(psys, "reactor_target_object") @@ -881,6 +879,15 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel): col = row.column() col.prop(part, "billboard_offset") + row = layout.row() + col = row.column() + col.prop(part, "billboard_size", text="Scale") + if part.billboard_align == 'VEL': + col = row.column(align=True) + col.label("Velocity Scale:") + col.prop(part, "billboard_velocity_head", text="Head") + col.prop(part, "billboard_velocity_tail", text="Tail") + if psys: col = layout.column() col.prop_search(psys, "billboard_normal_uv", ob.data, "uv_textures") diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 5da89d0090a..c7e3a9e7220 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -257,6 +257,7 @@ class PHYSICS_PT_domain_boundary(PhysicButtonsPanel, bpy.types.Panel): col.prop(fluid, "slip_type", text="") if fluid.slip_type == 'PARTIALSLIP': col.prop(fluid, "partial_slip_factor", slider=True, text="Amount") + col.prop(fluid, "surface_noobs") col = split.column() col.label(text="Surface:") diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index a3b10702fa7..6d36db29a6c 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -141,6 +141,7 @@ class RENDER_PT_layers(RenderButtonsPanel, bpy.types.Panel): col.prop(rl, "use_pass_uv") col.prop(rl, "use_pass_mist") col.prop(rl, "use_pass_object_index") + col.prop(rl, "use_pass_material_index") col.prop(rl, "use_pass_color") col = split.column() @@ -172,8 +173,130 @@ class RENDER_PT_layers(RenderButtonsPanel, bpy.types.Panel): row.prop(rl, "exclude_refraction", text="") +class RENDER_PT_dimensions(RenderButtonsPanel, bpy.types.Panel): + bl_label = "Dimensions" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + def draw(self, context): + layout = self.layout + + scene = context.scene + rd = scene.render + + row = layout.row(align=True) + row.menu("RENDER_MT_presets", text=bpy.types.RENDER_MT_presets.bl_label) + row.operator("render.preset_add", text="", icon="ZOOMIN") + row.operator("render.preset_add", text="", icon="ZOOMOUT").remove_active = True + + split = layout.split() + + col = split.column() + sub = col.column(align=True) + sub.label(text="Resolution:") + sub.prop(rd, "resolution_x", text="X") + sub.prop(rd, "resolution_y", text="Y") + sub.prop(rd, "resolution_percentage", text="") + + sub.label(text="Aspect Ratio:") + sub.prop(rd, "pixel_aspect_x", text="X") + sub.prop(rd, "pixel_aspect_y", text="Y") + + row = col.row() + row.prop(rd, "use_border", text="Border") + sub = row.row() + sub.active = rd.use_border + sub.prop(rd, "use_crop_to_border", text="Crop") + + col = split.column() + sub = col.column(align=True) + sub.label(text="Frame Range:") + sub.prop(scene, "frame_start", text="Start") + sub.prop(scene, "frame_end", text="End") + sub.prop(scene, "frame_step", text="Step") + + sub.label(text="Frame Rate:") + if rd.fps_base == 1: + fps_rate = round(rd.fps / rd.fps_base) + else: + fps_rate = round(rd.fps / rd.fps_base, 2) + + # TODO: Change the following to iterate over existing presets + custom_framerate = (fps_rate not in {23.98, 24, 25, 29.97, 30, 50, 59.94, 60}) + + if custom_framerate == True: + fps_label_text = "Custom (" + str(fps_rate) + " fps)" + else: + fps_label_text = str(fps_rate) + " fps" + + sub.menu("RENDER_MT_framerate_presets", text=fps_label_text) + + if custom_framerate or (bpy.types.RENDER_MT_framerate_presets.bl_label == "Custom"): + sub.prop(rd, "fps") + sub.prop(rd, "fps_base", text="/") + subrow = sub.row(align=True) + subrow.label(text="Time Remapping:") + subrow = sub.row(align=True) + subrow.prop(rd, "frame_map_old", text="Old") + subrow.prop(rd, "frame_map_new", text="New") + + +class RENDER_PT_antialiasing(RenderButtonsPanel, bpy.types.Panel): + bl_label = "Anti-Aliasing" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + def draw_header(self, context): + rd = context.scene.render + + self.layout.prop(rd, "use_antialiasing", text="") + + def draw(self, context): + layout = self.layout + + rd = context.scene.render + layout.active = rd.use_antialiasing + + split = layout.split() + + col = split.column() + col.row().prop(rd, "antialiasing_samples", expand=True) + sub = col.row() + sub.enabled = not rd.use_border + sub.prop(rd, "use_full_sample") + + col = split.column() + col.prop(rd, "pixel_filter_type", text="") + col.prop(rd, "filter_size", text="Size") + + +class RENDER_PT_motion_blur(RenderButtonsPanel, bpy.types.Panel): + bl_label = "Sampled Motion Blur" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + rd = context.scene.render + return not rd.use_full_sample and (rd.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + rd = context.scene.render + + self.layout.prop(rd, "use_motion_blur", text="") + + def draw(self, context): + layout = self.layout + + rd = context.scene.render + layout.active = rd.use_motion_blur + + row = layout.row() + row.prop(rd, "motion_blur_samples") + row.prop(rd, "motion_blur_shutter") + + class RENDER_PT_shading(RenderButtonsPanel, bpy.types.Panel): bl_label = "Shading" + bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER'} def draw(self, context): @@ -254,8 +377,7 @@ class RENDER_PT_post_processing(RenderButtonsPanel, bpy.types.Panel): col.prop(rd, "use_compositing") col.prop(rd, "use_sequencer") - col = split.column() - col.prop(rd, "dither_intensity", text="Dither", slider=True) + split.prop(rd, "dither_intensity", text="Dither", slider=True) layout.separator() @@ -276,6 +398,51 @@ class RENDER_PT_post_processing(RenderButtonsPanel, bpy.types.Panel): sub.prop(rd, "edge_color", text="") +class RENDER_PT_stamp(RenderButtonsPanel, bpy.types.Panel): + bl_label = "Stamp" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER'} + + def draw_header(self, context): + rd = context.scene.render + + self.layout.prop(rd, "use_stamp", text="") + + def draw(self, context): + layout = self.layout + + rd = context.scene.render + + layout.active = rd.use_stamp + + split = layout.split() + + col = split.column() + col.prop(rd, "use_stamp_time", text="Time") + col.prop(rd, "use_stamp_date", text="Date") + col.prop(rd, "use_stamp_render_time", text="RenderTime") + col.prop(rd, "use_stamp_frame", text="Frame") + col.prop(rd, "use_stamp_scene", text="Scene") + col.prop(rd, "use_stamp_camera", text="Camera") + col.prop(rd, "use_stamp_lens", text="Lens") + col.prop(rd, "use_stamp_filename", text="Filename") + col.prop(rd, "use_stamp_marker", text="Marker") + col.prop(rd, "use_stamp_sequencer_strip", text="Seq. Strip") + + col = split.column() + col.active = rd.use_stamp + col.prop(rd, "stamp_foreground", slider=True) + col.prop(rd, "stamp_background", slider=True) + col.separator() + col.prop(rd, "stamp_font_size", text="Font Size") + + row = layout.split(percentage=0.2) + row.prop(rd, "use_stamp_note", text="Note") + sub = row.row() + sub.active = rd.use_stamp_note + sub.prop(rd, "stamp_note_text", text="") + + class RENDER_PT_output(RenderButtonsPanel, bpy.types.Panel): bl_label = "Output" COMPAT_ENGINES = {'BLENDER_RENDER'} @@ -433,172 +600,6 @@ class RENDER_PT_encoding(RenderButtonsPanel, bpy.types.Panel): split.prop(rd, "ffmpeg_audio_volume", slider=True) -class RENDER_PT_antialiasing(RenderButtonsPanel, bpy.types.Panel): - bl_label = "Anti-Aliasing" - COMPAT_ENGINES = {'BLENDER_RENDER'} - - def draw_header(self, context): - rd = context.scene.render - - self.layout.prop(rd, "use_antialiasing", text="") - - def draw(self, context): - layout = self.layout - - rd = context.scene.render - layout.active = rd.use_antialiasing - - split = layout.split() - - col = split.column() - col.row().prop(rd, "antialiasing_samples", expand=True) - sub = col.row() - sub.enabled = not rd.use_border - sub.prop(rd, "use_full_sample") - - col = split.column() - col.prop(rd, "pixel_filter_type", text="") - col.prop(rd, "filter_size", text="Size") - - -class RENDER_PT_motion_blur(RenderButtonsPanel, bpy.types.Panel): - bl_label = "Sampled Motion Blur" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER'} - - @classmethod - def poll(cls, context): - rd = context.scene.render - return not rd.use_full_sample and (rd.engine in cls.COMPAT_ENGINES) - - def draw_header(self, context): - rd = context.scene.render - - self.layout.prop(rd, "use_motion_blur", text="") - - def draw(self, context): - layout = self.layout - - rd = context.scene.render - layout.active = rd.use_motion_blur - - row = layout.row() - row.prop(rd, "motion_blur_samples") - row.prop(rd, "motion_blur_shutter") - - -class RENDER_PT_dimensions(RenderButtonsPanel, bpy.types.Panel): - bl_label = "Dimensions" - COMPAT_ENGINES = {'BLENDER_RENDER'} - - def draw(self, context): - layout = self.layout - - scene = context.scene - rd = scene.render - - row = layout.row(align=True) - row.menu("RENDER_MT_presets", text=bpy.types.RENDER_MT_presets.bl_label) - row.operator("render.preset_add", text="", icon="ZOOMIN") - row.operator("render.preset_add", text="", icon="ZOOMOUT").remove_active = True - - split = layout.split() - - col = split.column() - sub = col.column(align=True) - sub.label(text="Resolution:") - sub.prop(rd, "resolution_x", text="X") - sub.prop(rd, "resolution_y", text="Y") - sub.prop(rd, "resolution_percentage", text="") - - sub.label(text="Aspect Ratio:") - sub.prop(rd, "pixel_aspect_x", text="X") - sub.prop(rd, "pixel_aspect_y", text="Y") - - row = col.row() - row.prop(rd, "use_border", text="Border") - sub = row.row() - sub.active = rd.use_border - sub.prop(rd, "use_crop_to_border", text="Crop") - - col = split.column() - sub = col.column(align=True) - sub.label(text="Frame Range:") - sub.prop(scene, "frame_start", text="Start") - sub.prop(scene, "frame_end", text="End") - sub.prop(scene, "frame_step", text="Step") - - sub.label(text="Frame Rate:") - if rd.fps_base == 1: - fps_rate = round(rd.fps / rd.fps_base) - else: - fps_rate = round(rd.fps / rd.fps_base, 2) - - # TODO: Change the following to iterate over existing presets - custom_framerate = (fps_rate not in {23.98, 24, 25, 29.97, 30, 50, 59.94, 60}) - - if custom_framerate == True: - fps_label_text = "Custom (" + str(fps_rate) + " fps)" - else: - fps_label_text = str(fps_rate) + " fps" - - sub.menu("RENDER_MT_framerate_presets", text=fps_label_text) - - if custom_framerate or (bpy.types.RENDER_MT_framerate_presets.bl_label == "Custom"): - sub.prop(rd, "fps") - sub.prop(rd, "fps_base", text="/") - subrow = sub.row(align=True) - subrow.label(text="Time Remapping:") - subrow = sub.row(align=True) - subrow.prop(rd, "frame_map_old", text="Old") - subrow.prop(rd, "frame_map_new", text="New") - - -class RENDER_PT_stamp(RenderButtonsPanel, bpy.types.Panel): - bl_label = "Stamp" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER'} - - def draw_header(self, context): - rd = context.scene.render - - self.layout.prop(rd, "use_stamp", text="") - - def draw(self, context): - layout = self.layout - - rd = context.scene.render - - layout.active = rd.use_stamp - - split = layout.split() - - col = split.column() - col.prop(rd, "use_stamp_time", text="Time") - col.prop(rd, "use_stamp_date", text="Date") - col.prop(rd, "use_stamp_render_time", text="RenderTime") - col.prop(rd, "use_stamp_frame", text="Frame") - col.prop(rd, "use_stamp_scene", text="Scene") - col.prop(rd, "use_stamp_camera", text="Camera") - col.prop(rd, "use_stamp_lens", text="Lens") - col.prop(rd, "use_stamp_filename", text="Filename") - col.prop(rd, "use_stamp_marker", text="Marker") - col.prop(rd, "use_stamp_sequencer_strip", text="Seq. Strip") - - col = split.column() - col.active = rd.use_stamp - col.prop(rd, "stamp_foreground", slider=True) - col.prop(rd, "stamp_background", slider=True) - col.separator() - col.prop(rd, "stamp_font_size", text="Font Size") - - row = layout.split(percentage=0.2) - row.prop(rd, "use_stamp_note", text="Note") - sub = row.row() - sub.active = rd.use_stamp_note - sub.prop(rd, "stamp_note_text", text="") - - class RENDER_PT_bake(RenderButtonsPanel, bpy.types.Panel): bl_label = "Bake" bl_options = {'DEFAULT_CLOSED'} @@ -613,29 +614,42 @@ class RENDER_PT_bake(RenderButtonsPanel, bpy.types.Panel): layout.prop(rd, "bake_type") - if rd.bake_type == 'NORMALS': - layout.prop(rd, "bake_normal_space") - elif rd.bake_type in {'DISPLACEMENT', 'AO'}: - layout.prop(rd, "use_bake_normalize") + multires_bake = False + if rd.bake_type in ['NORMALS', 'DISPLACEMENT']: + layout.prop(rd, 'use_bake_multires') + multires_bake = rd.use_bake_multires - # col.prop(rd, "bake_aa_mode") - # col.prop(rd, "use_bake_antialiasing") + if not multires_bake: + if rd.bake_type == 'NORMALS': + layout.prop(rd, "bake_normal_space") + elif rd.bake_type in {'DISPLACEMENT', 'AO'}: + layout.prop(rd, "use_bake_normalize") - layout.separator() + # col.prop(rd, "bake_aa_mode") + # col.prop(rd, "use_bake_antialiasing") - split = layout.split() + layout.separator() - col = split.column() - col.prop(rd, "use_bake_clear") - col.prop(rd, "bake_margin") - col.prop(rd, "bake_quad_split", text="Split") + split = layout.split() + + col = split.column() + col.prop(rd, "use_bake_clear") + col.prop(rd, "bake_margin") + col.prop(rd, "bake_quad_split", text="Split") + + col = split.column() + col.prop(rd, "use_bake_selected_to_active") + sub = col.column() + sub.active = rd.use_bake_selected_to_active + sub.prop(rd, "bake_distance") + sub.prop(rd, "bake_bias") + else: + if rd.bake_type == 'DISPLACEMENT': + layout.prop(rd, "use_bake_lores_mesh") + + layout.prop(rd, "use_bake_clear") + layout.prop(rd, "bake_margin") - col = split.column() - col.prop(rd, "use_bake_selected_to_active") - sub = col.column() - sub.active = rd.use_bake_selected_to_active - sub.prop(rd, "bake_distance") - sub.prop(rd, "bake_bias") if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 01890bc3c99..7ca8818cbd2 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -88,15 +88,15 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, bpy.types.Panel): @classmethod def poll(cls, context): engine = context.scene.render.engine - if not hasattr(context, "texture_slot"): + if not (hasattr(context, "texture_slot") or hasattr(context, "texture_node")): return False return ((context.material or context.world or context.lamp or context.brush or context.texture or context.particle_system or isinstance(context.space_data.pin_id, bpy.types.ParticleSettings)) and (engine in cls.COMPAT_ENGINES)) def draw(self, context): layout = self.layout - slot = context.texture_slot - node = context.texture_node + slot = getattr(context, "texture_slot", None) + node = getattr(context, "texture_node", None) space = context.space_data tex = context.texture idblock = context_tex_datablock(context) @@ -208,7 +208,7 @@ class TextureSlotPanel(TextureButtonsPanel): return False engine = context.scene.render.engine - return TextureButtonsPanel.poll(self, context) and (engine in cls.COMPAT_ENGINES) + return TextureButtonsPanel.poll(cls, context) and (engine in cls.COMPAT_ENGINES) # Texture Type Panels # @@ -393,7 +393,7 @@ class TEXTURE_PT_image_sampling(TextureTypePanel, bpy.types.Panel): idblock = context_tex_datablock(context) tex = context.texture - slot = context.texture_slot + slot = getattr(context, "texture_slot", None) split = layout.split() @@ -408,7 +408,7 @@ class TEXTURE_PT_image_sampling(TextureTypePanel, bpy.types.Panel): col = split.column() #Only for Material based textures, not for Lamp/World... - if isinstance(idblock, bpy.types.Material): + if slot and isinstance(idblock, bpy.types.Material): col.prop(tex, "use_normal_map") row = col.row() row.active = tex.use_normal_map diff --git a/release/scripts/startup/bl_ui/properties_world.py b/release/scripts/startup/bl_ui/properties_world.py index 4f398c9fbd9..c577af01374 100644 --- a/release/scripts/startup/bl_ui/properties_world.py +++ b/release/scripts/startup/bl_ui/properties_world.py @@ -20,8 +20,6 @@ import bpy from rna_prop_ui import PropertyPanel -# TODO, "color_range" not in the UI - class WorldButtonsPanel(): bl_space_type = 'PROPERTIES' @@ -96,6 +94,10 @@ class WORLD_PT_world(WorldButtonsPanel, bpy.types.Panel): col.active = world.use_sky_blend row.column().prop(world, "ambient_color") + row = layout.row() + row.prop(world, "exposure") + row.prop(world, "color_range") + class WORLD_PT_ambient_occlusion(WorldButtonsPanel, bpy.types.Panel): bl_label = "Ambient Occlusion" diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 77583b80824..fa5579ea2e0 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -198,6 +198,10 @@ class IMAGE_MT_uvs_transform(bpy.types.Menu): layout.operator("transform.rotate") layout.operator("transform.resize") + layout.separator() + + layout.operator("transform.shear") + class IMAGE_MT_uvs_snap(bpy.types.Menu): bl_label = "Snap" @@ -434,9 +438,9 @@ class IMAGE_PT_game_properties(bpy.types.Panel): @classmethod def poll(cls, context): - rd = context.scene.render sima = context.space_data - return (sima and sima.image) and (rd.engine == 'BLENDER_GAME') + # display even when not in game mode because these settings effect the 3d view + return (sima and sima.image) # and (rd.engine == 'BLENDER_GAME') def draw(self, context): layout = self.layout @@ -615,10 +619,9 @@ class IMAGE_PT_view_properties(bpy.types.Panel): split = layout.split() col = split.column() + col.prop(uvedit, "show_faces") col.prop(uvedit, "show_smooth_edges", text="Smooth") col.prop(uvedit, "show_modified_edges", text="Modified") - #col.prop(uvedit, "show_edges") - #col.prop(uvedit, "show_faces") col = split.column() col.prop(uvedit, "show_stretch", text="Stretch") diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 1fb2e5b735e..f66cee7f431 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -61,7 +61,9 @@ class INFO_HT_header(bpy.types.Header): layout.template_reports_banner() - layout.label(text=scene.statistics()) + row = layout.row(align=True) + row.operator("wm.splash", text="", icon='BLENDER', emboss=False) + row.label(text=scene.statistics()) # XXX: this should be right-aligned to the RHS of the region layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER', text="") @@ -350,7 +352,7 @@ class INFO_MT_help(bpy.types.Menu): layout = self.layout layout.operator("wm.url_open", text="Manual", icon='HELP').url = 'http://wiki.blender.org/index.php/Doc:Manual' - layout.operator("wm.url_open", text="Release Log", icon='URL').url = 'http://www.blender.org/development/release-logs/blender-257/' + layout.operator("wm.url_open", text="Release Log", icon='URL').url = 'http://www.blender.org/development/release-logs/blender-258/' layout.separator() diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index fed1cc49c4c..2088d8798f2 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -138,6 +138,7 @@ class NODE_MT_node(bpy.types.Menu): layout.operator("node.duplicate_move") layout.operator("node.delete") + layout.operator("node.delete_reconnect") layout.separator() layout.operator("node.link_make") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 2079aef6402..c477a2ff62b 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -42,7 +42,7 @@ class SEQUENCER_HT_header(bpy.types.Header): sub = row.row(align=True) sub.menu("SEQUENCER_MT_view") - if (st.view_type == 'SEQUENCER') or (st.view_type == 'SEQUENCER_PREVIEW'): + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: sub.menu("SEQUENCER_MT_select") sub.menu("SEQUENCER_MT_marker") sub.menu("SEQUENCER_MT_add") @@ -50,17 +50,17 @@ class SEQUENCER_HT_header(bpy.types.Header): layout.prop(st, "view_type", expand=True, text="") - if (st.view_type == 'PREVIEW') or (st.view_type == 'SEQUENCER_PREVIEW'): + if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: layout.prop(st, "display_mode", expand=True, text="") - if (st.view_type == 'SEQUENCER'): + if st.view_type == 'SEQUENCER': row = layout.row(align=True) row.operator("sequencer.copy", text="", icon='COPYDOWN') row.operator("sequencer.paste", text="", icon='PASTEDOWN') layout.separator() layout.operator("sequencer.refresh_all") - elif (st.view_type == 'SEQUENCER_PREVIEW'): + elif st.view_type == 'SEQUENCER_PREVIEW': layout.separator() layout.operator("sequencer.refresh_all") layout.prop(st, "display_channel", text="Channel") @@ -101,9 +101,9 @@ class SEQUENCER_MT_view(bpy.types.Menu): layout.separator() - if (st.view_type == 'SEQUENCER') or (st.view_type == 'SEQUENCER_PREVIEW'): + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: layout.operator("sequencer.view_all", text='View all Sequences') - if (st.view_type == 'PREVIEW') or (st.view_type == 'SEQUENCER_PREVIEW'): + if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}: layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.operator("sequencer.view_all_preview", text='Fit preview in window') layout.operator("sequencer.view_zoom_ratio", text='Show preview 1:1').ratio = 1.0 @@ -213,6 +213,7 @@ class SEQUENCER_MT_add_effect(bpy.types.Menu): layout.operator("sequencer.effect_strip_add", text="Color").type = 'COLOR' layout.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED' layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM' + layout.operator("sequencer.effect_strip_add", text="Adjustment Layer").type = 'ADJUSTMENT' class SEQUENCER_MT_strip(bpy.types.Menu): @@ -299,7 +300,7 @@ class SequencerButtonsPanel(): @staticmethod def has_sequencer(context): - return (context.space_data.view_type == 'SEQUENCER') or (context.space_data.view_type == 'SEQUENCER_PREVIEW') + return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}) @classmethod def poll(cls, context): @@ -312,7 +313,7 @@ class SequencerButtonsPanel_Output(): @staticmethod def has_preview(context): - return (context.space_data.view_type == 'PREVIEW') or (context.space_data.view_type == 'SEQUENCER_PREVIEW') + return (context.space_data.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) @classmethod def poll(cls, context): @@ -391,7 +392,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, bpy.types.Panel): 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED', - 'MULTICAM'} + 'MULTICAM', 'ADJUSTMENT'} def draw(self, context): layout = self.layout @@ -530,7 +531,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, bpy.types.Panel): 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', - 'MULTICAM', 'SPEED'} + 'MULTICAM', 'SPEED', 'ADJUSTMENT'} def draw(self, context): layout = self.layout @@ -656,11 +657,17 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, bpy.types.Panel): layout.template_ID(strip, "scene") + scene = strip.scene + if scene: + layout.prop(scene.render, "use_sequencer") + layout.label(text="Camera Override") layout.template_ID(strip, "scene_camera") - sce = strip.scene - layout.label(text="Original frame range: %d-%d (%d)" % (sce.frame_start, sce.frame_end, sce.frame_end - sce.frame_start + 1)) + if scene: + sta = scene.frame_start + end = scene.frame_end + layout.label(text="Original frame range: %d-%d (%d)" % (sta, end, end - sta + 1)) class SEQUENCER_PT_filter(SequencerButtonsPanel, bpy.types.Panel): @@ -680,7 +687,7 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, bpy.types.Panel): 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', - 'MULTICAM', 'SPEED'} + 'MULTICAM', 'SPEED', 'ADJUSTMENT'} def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index 3d3fc8499af..b787fc5cf75 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> import bpy @@ -33,19 +33,21 @@ class TEXT_HT_header(bpy.types.Header): row.template_header() if context.area.show_menus: - sub = row.row(align=True) - sub.menu("TEXT_MT_view") - sub.menu("TEXT_MT_text") + row.menu("TEXT_MT_view") + row.menu("TEXT_MT_text") + if text: - sub.menu("TEXT_MT_edit") - sub.menu("TEXT_MT_format") + row.menu("TEXT_MT_edit") + row.menu("TEXT_MT_format") + + row.menu("TEXT_MT_templates") if text and text.is_modified: - row = layout.row() - # row.color(redalert) - row.operator("text.resolve_conflict", text="", icon='HELP') + sub = row.row() + sub.alert = True + sub.operator("text.resolve_conflict", text="", icon='HELP') - layout.template_ID(st, "text", new="text.new", unlink="text.unlink") + row.template_ID(st, "text", new="text.new", unlink="text.unlink") row = layout.row(align=True) row.prop(st, "show_line_numbers", text="") @@ -63,11 +65,13 @@ class TEXT_HT_header(bpy.types.Header): row = layout.row() if text.filepath: if text.is_dirty: - row.label(text="File: *%s (unsaved)" % text.filepath) + row.label(text="File: *%r (unsaved)" % text.filepath) else: - row.label(text="File: %s" % text.filepath) + row.label(text="File: %r" % text.filepath) else: - row.label(text="Text: External" if text.library else "Text: Internal") + row.label(text="Text: External" + if text.library + else "Text: Internal") class TEXT_PT_properties(bpy.types.Panel): @@ -129,6 +133,7 @@ class TEXT_PT_find(bpy.types.Panel): layout.operator("text.mark_all") # settings + layout.prop(st, "use_match_case") row = layout.row() row.prop(st, "use_find_wrap", text="Wrap") row.prop(st, "use_find_all", text="All") @@ -149,8 +154,12 @@ class TEXT_MT_view(bpy.types.Menu): layout.separator() - layout.operator("text.move", text="Top of File").type = 'FILE_TOP' - layout.operator("text.move", text="Bottom of File").type = 'FILE_BOTTOM' + layout.operator("text.move", + text="Top of File", + ).type = 'FILE_TOP' + layout.operator("text.move", + text="Bottom of File", + ).type = 'FILE_BOTTOM' class TEXT_MT_text(bpy.types.Menu): @@ -184,19 +193,15 @@ class TEXT_MT_text(bpy.types.Menu): # XXX uiMenuItemO(head, 0, "text.refresh_pyconstraints"); #endif - layout.separator() - - layout.menu("TEXT_MT_templates") - class TEXT_MT_templates(bpy.types.Menu): - ''' - Creates the menu items by scanning scripts/templates - ''' - bl_label = "Script Templates" + bl_label = "Templates" def draw(self, context): - self.path_menu(bpy.utils.script_paths("templates"), "text.open", {"internal": True}) + self.path_menu(bpy.utils.script_paths("templates"), + "text.open", + {"internal": True}, + ) class TEXT_MT_edit_select(bpy.types.Menu): @@ -245,8 +250,12 @@ class TEXT_MT_edit_to3d(bpy.types.Menu): def draw(self, context): layout = self.layout - layout.operator("text.to_3d_object", text="One Object").split_lines = False - layout.operator("text.to_3d_object", text="One Object Per Line").split_lines = True + layout.operator("text.to_3d_object", + text="One Object", + ).split_lines = False + layout.operator("text.to_3d_object", + text="One Object Per Line", + ).split_lines = True class TEXT_MT_edit(bpy.types.Menu): diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index bf396e98c79..576709c6072 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -94,7 +94,7 @@ class USERPREF_HT_header(bpy.types.Header): layout.operator("wm.keyconfig_import") elif userpref.active_section == 'ADDONS': layout.operator("wm.addon_install") - layout.menu("USERPREF_MT_addons_dev_guides", text=" Addons Developer Guides", icon='INFO') + layout.menu("USERPREF_MT_addons_dev_guides") elif userpref.active_section == 'THEMES': layout.operator("ui.reset_default_theme") @@ -126,7 +126,7 @@ class USERPREF_MT_appconfigs(bpy.types.Menu): preset_operator = "wm.appconfig_activate" def draw(self, context): - props = self.layout.operator("wm.appconfig_default", text="Blender (default)") + self.layout.operator("wm.appconfig_default", text="Blender (default)") # now draw the presets bpy.types.Menu.draw_preset(self, context) @@ -199,6 +199,7 @@ class USERPREF_PT_interface(bpy.types.Panel): col.prop(view, "use_zoom_to_mouse") col.prop(view, "use_rotate_around_active") col.prop(view, "use_global_pivot") + col.prop(view, "use_camera_lock_parent") col.separator() @@ -437,6 +438,8 @@ class USERPREF_PT_system(bpy.types.Panel): col.label(text="OpenGL:") col.prop(system, "gl_clip_alpha", slider=True) col.prop(system, "use_mipmaps") + col.label(text="Anisotropic Filtering") + col.prop(system, "anisotropic_filter", text="") col.prop(system, "use_vertex_buffer_objects") #Anti-aliasing is disabled as it breaks broder/lasso select #col.prop(system, "use_antialiasing") @@ -752,7 +755,7 @@ class USERPREF_PT_file(bpy.types.Panel): from bl_ui.space_userpref_keymap import InputKeyMapPanel -class USERPREF_PT_input(InputKeyMapPanel): +class USERPREF_PT_input(bpy.types.Panel, InputKeyMapPanel): bl_space_type = 'USER_PREFERENCES' bl_label = "Input" @@ -846,17 +849,14 @@ class USERPREF_PT_input(InputKeyMapPanel): class USERPREF_MT_addons_dev_guides(bpy.types.Menu): - bl_label = "Addons develoment guides" + bl_label = "Development Guides" # menu to open webpages with addons development guides def draw(self, context): layout = self.layout - layout.operator('wm.url_open', text='API Concepts' - ).url = 'http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro' - layout.operator('wm.url_open', text='Addons guidelines', - ).url = 'http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons' - layout.operator('wm.url_open', text='How to share your addon', - ).url = 'http://wiki.blender.org/index.php/Dev:Py/Sharing' + layout.operator('wm.url_open', text='API Concepts', icon='URL').url = 'http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro' + layout.operator('wm.url_open', text='Addon Guidelines', icon='URL').url = 'http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Guidelines/Addons' + layout.operator('wm.url_open', text='How to share your addon', icon='URL').url = 'http://wiki.blender.org/index.php/Dev:Py/Sharing' class USERPREF_PT_addons(bpy.types.Panel): @@ -876,6 +876,29 @@ class USERPREF_PT_addons(bpy.types.Panel): def module_get(mod_name): return USERPREF_PT_addons._addons_fake_modules[mod_name] + @staticmethod + def is_user_addon(mod, user_addon_paths): + if not user_addon_paths: + user_script_path = bpy.utils.user_script_path() + if user_script_path is not None: + user_addon_paths.append(os.path.join(user_script_path(), "addons")) + user_addon_paths.append(os.path.join(bpy.utils.resource_path('USER'), "scripts", "addons")) + + for path in user_addon_paths: + if bpy.path.is_subdir(mod.__file__, path): + return True + return False + + @staticmethod + def draw_error(layout, message): + lines = message.split("\n") + box = layout.box() + rowsub = box.row() + rowsub.label(lines[0]) + rowsub.label(icon='ERROR') + for l in lines[1:]: + box.label(l) + def draw(self, context): layout = self.layout @@ -888,6 +911,7 @@ class USERPREF_PT_addons(bpy.types.Panel): split = layout.split(percentage=0.2) col = split.column() col.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM') + col.label(text="Categories") col.prop(context.window_manager, "addon_filter", expand=True) col.label(text="Supported Level") @@ -895,10 +919,21 @@ class USERPREF_PT_addons(bpy.types.Panel): col = split.column() + # set in addon_utils.modules(...) + if addon_utils.error_duplicates: + self.draw_error(col, + "Multiple addons using the same name found!\n" + "likely a problem with the script search path.\n" + "(see console for details)", + ) + filter = context.window_manager.addon_filter search = context.window_manager.addon_search.lower() support = context.window_manager.addon_support + # initialized on demand + user_addon_paths = [] + for mod, info in addons: module_name = mod.__name__ @@ -968,18 +1003,21 @@ class USERPREF_PT_addons(bpy.types.Panel): split = colsub.row().split(percentage=0.15) split.label(text="Warning:") split.label(text=' ' + info["warning"], icon='ERROR') - if info["wiki_url"] or info["tracker_url"]: + + user_addon = USERPREF_PT_addons.is_user_addon(mod, user_addon_paths) + tot_row = bool(info["wiki_url"]) + bool(info["tracker_url"]) + bool(user_addon) + + if tot_row: split = colsub.row().split(percentage=0.15) split.label(text="Internet:") if info["wiki_url"]: split.operator("wm.url_open", text="Link to the Wiki", icon='HELP').url = info["wiki_url"] if info["tracker_url"]: split.operator("wm.url_open", text="Report a Bug", icon='URL').url = info["tracker_url"] + if user_addon: + split.operator("wm.addon_remove", text="Remove", icon='CANCEL').module = mod.__name__ - if info["wiki_url"] and info["tracker_url"]: - split.separator() - else: - split.separator() + for i in range(4 - tot_row): split.separator() # Append missing scripts @@ -1103,7 +1141,6 @@ class WM_OT_addon_install(bpy.types.Operator): del pyfile_dir # done checking for exceptional case - addon_files_old = set(os.listdir(path_addons)) addons_old = {mod.__name__ for mod in addon_utils.modules(USERPREF_PT_addons._addons_fake_modules)} #check to see if the file is in compressed format (.zip) @@ -1116,7 +1153,7 @@ class WM_OT_addon_install(bpy.types.Operator): if self.overwrite: for f in file_to_extract.namelist(): - __class__._module_remove(path_addons, f) + WM_OT_addon_install._module_remove(path_addons, f) else: for f in file_to_extract.namelist(): path_dest = os.path.join(path_addons, os.path.basename(f)) @@ -1140,7 +1177,7 @@ class WM_OT_addon_install(bpy.types.Operator): path_dest = os.path.join(path_addons, os.path.basename(pyfile)) if self.overwrite: - __class__._module_remove(path_addons, os.path.basename(pyfile)) + WM_OT_addon_install._module_remove(path_addons, os.path.basename(pyfile)) elif os.path.exists(path_dest): self.report({'WARNING'}, "File already installed to %r\n" % path_dest) return {'CANCELLED'} @@ -1185,6 +1222,54 @@ class WM_OT_addon_install(bpy.types.Operator): return {'RUNNING_MODAL'} +class WM_OT_addon_remove(bpy.types.Operator): + "Disable an addon" + bl_idname = "wm.addon_remove" + bl_label = "Remove Add-On" + + module = StringProperty(name="Module", description="Module name of the addon to remove") + + @staticmethod + def path_from_addon(module): + for mod in addon_utils.modules(USERPREF_PT_addons._addons_fake_modules): + if mod.__name__ == module: + filepath = mod.__file__ + if os.path.exists(filepath): + if os.path.splitext(os.path.basename(filepath))[0] == "__init__": + return os.path.dirname(filepath), True + else: + return filepath, False + return None, False + + def execute(self, context): + path, isdir = WM_OT_addon_remove.path_from_addon(self.module) + if path is None: + self.report('WARNING', "Addon path %r could not be found" % path) + return {'CANCELLED'} + + # incase its enabled + addon_utils.disable(self.module) + + import shutil + if isdir: + shutil.rmtree(path) + else: + os.remove(path) + + context.area.tag_redraw() + return {'FINISHED'} + + # lame confirmation check + def draw(self, context): + self.layout.label(text="Remove Addon: %r?" % self.module) + path, isdir = WM_OT_addon_remove.path_from_addon(self.module) + self.layout.label(text="Path: %r" % path) + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_props_dialog(self, width=600) + + class WM_OT_addon_expand(bpy.types.Operator): "Display more information on this add-on" bl_idname = "wm.addon_expand" diff --git a/release/scripts/startup/bl_ui/space_userpref_keymap.py b/release/scripts/startup/bl_ui/space_userpref_keymap.py index 378fe231091..85764c55304 100644 --- a/release/scripts/startup/bl_ui/space_userpref_keymap.py +++ b/release/scripts/startup/bl_ui/space_userpref_keymap.py @@ -138,7 +138,7 @@ class USERPREF_MT_keyconfigs(bpy.types.Menu): bpy.types.Menu.draw_preset(self, context) -class InputKeyMapPanel(bpy.types.Panel): +class InputKeyMapPanel: bl_space_type = 'USER_PREFERENCES' bl_label = "Input" bl_region_type = 'WINDOW' @@ -189,9 +189,9 @@ class InputKeyMapPanel(bpy.types.Panel): if km.is_modal: row.label(text="", icon='LINKED') if km.is_user_defined: - op = row.operator("wm.keymap_restore", text="Restore") + row.operator("wm.keymap_restore", text="Restore") else: - op = row.operator("wm.keymap_edit", text="Edit") + row.operator("wm.keymap_edit", text="Edit") if km.show_expanded_children: if children: @@ -213,7 +213,7 @@ class InputKeyMapPanel(bpy.types.Panel): col = self.indented_layout(col, level + 1) subcol = col.split(percentage=0.2).column() subcol.enabled = km.is_user_defined - op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') + subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') col.separator() @@ -234,7 +234,7 @@ class InputKeyMapPanel(bpy.types.Panel): for pname, value in properties.bl_rna.properties.items(): if pname != "rna_type" and not properties.is_property_hidden(pname): if isinstance(value, bpy.types.OperatorProperties): - __class__.draw_kmi_properties(box, value, title=pname) + InputKeyMapPanel.draw_kmi_properties(box, value, title=pname) else: flow.prop(properties, pname) @@ -325,7 +325,7 @@ class InputKeyMapPanel(bpy.types.Panel): # Operator properties props = kmi.properties if props is not None: - __class__.draw_kmi_properties(box, props) + InputKeyMapPanel.draw_kmi_properties(box, props) # Modal key maps attached to this operator if not km.is_modal: @@ -351,9 +351,9 @@ class InputKeyMapPanel(bpy.types.Panel): row.label() if km.is_user_defined: - op = row.operator("wm.keymap_restore", text="Restore") + row.operator("wm.keymap_restore", text="Restore") else: - op = row.operator("wm.keymap_edit", text="Edit") + row.operator("wm.keymap_edit", text="Edit") for kmi in filtered_items: self.draw_kmi(display_keymaps, kc, km, kmi, col, 1) @@ -362,7 +362,7 @@ class InputKeyMapPanel(bpy.types.Panel): col = self.indented_layout(layout, 1) subcol = col.split(percentage=0.2).column() subcol.enabled = km.is_user_defined - op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') + subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') def draw_hierarchy(self, display_keymaps, layout): for entry in KM_HIERARCHY: @@ -411,8 +411,8 @@ def export_properties(prefix, properties, lines=None): if lines is None: lines = [] - for pname in properties.keys(): - if not properties.is_property_hidden(pname): + for pname in properties.bl_rna.properties.keys(): + if pname != "rna_type" and not properties.is_property_hidden(pname): value = getattr(properties, pname) if isinstance(value, bpy.types.OperatorProperties): export_properties(prefix + "." + pname, value, lines) @@ -723,9 +723,7 @@ class WM_OT_keyitem_add(bpy.types.Operator): bl_label = "Add Key Map Item" def execute(self, context): - wm = context.window_manager km = context.keymap - kc = wm.keyconfigs.default if km.is_modal: km.keymap_items.new_modal("", 'A', 'PRESS') # kmi diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 7d31bc39b0a..083c330f61d 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -54,21 +54,13 @@ class VIEW3D_HT_header(bpy.types.Header): sub.menu("VIEW3D_MT_object") row = layout.row() + # Contains buttons like Mode, Pivot, Manipulator, Layer, Mesh Select Mode... row.template_header_3D() - # do in C for now since these buttons cant be both toggle AND exclusive. - ''' - if obj and obj.mode == 'EDIT' and obj.type == 'MESH': - row_sub = row.row(align=True) - row_sub.prop(toolsettings, "mesh_select_mode", text="", index=0, icon='VERTEXSEL') - row_sub.prop(toolsettings, "mesh_select_mode", text="", index=1, icon='EDGESEL') - row_sub.prop(toolsettings, "mesh_select_mode", text="", index=2, icon='FACESEL') - ''' - if obj: # Particle edit if obj.mode == 'PARTICLE_EDIT': - row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True, toggle=True) + row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True) # Occlude geometry if view.viewport_shade in {'SOLID', 'SHADED', 'TEXTURED'} and (obj.mode == 'PARTICLE_EDIT' or (obj.mode == 'EDIT' and obj.type == 'MESH')): @@ -87,16 +79,21 @@ class VIEW3D_HT_header(bpy.types.Header): row.prop(toolsettings, "proportional_edit_falloff", text="", icon_only=True) # Snap + snap_element = toolsettings.snap_element row = layout.row(align=True) row.prop(toolsettings, "use_snap", text="") row.prop(toolsettings, "snap_element", text="", icon_only=True) - if toolsettings.snap_element != 'INCREMENT': + if snap_element != 'INCREMENT': row.prop(toolsettings, "snap_target", text="") - if obj and obj.mode == 'OBJECT': - row.prop(toolsettings, "use_snap_align_rotation", text="") - if toolsettings.snap_element == 'VOLUME': + if obj: + if obj.mode == 'OBJECT': + row.prop(toolsettings, "use_snap_align_rotation", text="") + elif obj.mode == 'EDIT': + row.prop(toolsettings, "use_snap_self", text="") + + if snap_element == 'VOLUME': row.prop(toolsettings, "use_snap_peel_object", text="") - elif toolsettings.snap_element == 'FACE': + elif snap_element == 'FACE': row.prop(toolsettings, "use_snap_project", text="") # OpenGL render @@ -685,6 +682,7 @@ class VIEW3D_MT_object(bpy.types.Menu): layout.operator("ed.undo") layout.operator("ed.redo") + layout.operator("ed.undo_history") layout.separator() @@ -771,10 +769,16 @@ class VIEW3D_MT_object_specials(bpy.types.Menu): if obj.type == 'CAMERA': layout.operator_context = 'INVOKE_REGION_WIN' - props = layout.operator("wm.context_modal_mouse", text="Camera Lens Angle") - props.data_path_iter = "selected_editable_objects" - props.data_path_item = "data.lens" - props.input_scale = 0.1 + if obj.data.type == 'PERSP': + props = layout.operator("wm.context_modal_mouse", text="Camera Lens Angle") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.lens" + props.input_scale = 0.1 + else: + props = layout.operator("wm.context_modal_mouse", text="Camera Lens Scale") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.ortho_scale" + props.input_scale = 0.01 if not obj.data.dof_object: #layout.label(text="Test Has DOF obj"); @@ -1049,6 +1053,7 @@ class VIEW3D_MT_paint_weight(bpy.types.Menu): layout.operator("ed.undo") layout.operator("ed.redo") + layout.operator("ed.undo_history") layout.separator() @@ -1096,17 +1101,18 @@ class VIEW3D_MT_sculpt(bpy.types.Menu): layout.operator_menu_enum("brush.curve_preset", "shape") layout.separator() - sculpt_tool = brush.sculpt_tool + if brush is not None: # unlikely but can happen + sculpt_tool = brush.sculpt_tool - if sculpt_tool != 'GRAB': - layout.prop_menu_enum(brush, "stroke_method") + if sculpt_tool != 'GRAB': + layout.prop_menu_enum(brush, "stroke_method") - if sculpt_tool in {'DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'}: - layout.prop_menu_enum(brush, "direction") + if sculpt_tool in {'DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'}: + layout.prop_menu_enum(brush, "direction") - if sculpt_tool == 'LAYER': - layout.prop(brush, "use_persistent") - layout.operator("sculpt.set_persistent_base") + if sculpt_tool == 'LAYER': + layout.prop(brush, "use_persistent") + layout.operator("sculpt.set_persistent_base") layout.separator() layout.prop(sculpt, "use_threaded", text="Threaded Sculpt") @@ -1129,6 +1135,7 @@ class VIEW3D_MT_particle(bpy.types.Menu): layout.operator("ed.undo") layout.operator("ed.redo") + layout.operator("ed.undo_history") layout.separator() @@ -1182,6 +1189,7 @@ class VIEW3D_MT_pose(bpy.types.Menu): layout.operator("ed.undo") layout.operator("ed.redo") + layout.operator("ed.undo_history") layout.separator() @@ -1239,7 +1247,7 @@ class VIEW3D_MT_pose(bpy.types.Menu): layout.separator() layout.menu("VIEW3D_MT_pose_showhide") - layout.operator_menu_enum("pose.flags_set", 'mode', text="Bone Settings") + layout.menu("VIEW3D_MT_bone_options_toggle", text="Bone Settings") class VIEW3D_MT_pose_transform(bpy.types.Menu): @@ -1360,6 +1368,49 @@ class VIEW3D_MT_pose_apply(bpy.types.Menu): layout.operator("pose.visual_transform_apply") +class BoneOptions: + def draw(self, context): + layout = self.layout + + options = [ + "show_wire", + "use_deform", + "use_envelope_multiply", + "use_inherit_rotation", + "use_inherit_scale", + ] + + if context.mode == 'EDIT_ARMATURE': + bone_props = bpy.types.EditBone.bl_rna.properties + data_path_iter = "selected_bones" + opt_suffix = "" + options.append("lock") + else: # posemode + bone_props = bpy.types.Bone.bl_rna.properties + data_path_iter = "selected_pose_bones" + opt_suffix = "bone." + + for opt in options: + props = layout.operator("wm.context_collection_boolean_set", text=bone_props[opt].name) + props.data_path_iter = data_path_iter + props.data_path_item = opt_suffix + opt + props.type = self.type + + +class VIEW3D_MT_bone_options_toggle(bpy.types.Menu, BoneOptions): + bl_label = "Toggle Bone Options" + type = 'TOGGLE' + + +class VIEW3D_MT_bone_options_enable(bpy.types.Menu, BoneOptions): + bl_label = "Enable Bone Options" + type = 'ENABLE' + + +class VIEW3D_MT_bone_options_disable(bpy.types.Menu, BoneOptions): + bl_label = "Disable Bone Options" + type = 'DISABLE' + # ********** Edit Menus, suffix from ob.type ********** @@ -1373,6 +1424,7 @@ class VIEW3D_MT_edit_mesh(bpy.types.Menu): layout.operator("ed.undo") layout.operator("ed.redo") + layout.operator("ed.undo_history") layout.separator() @@ -1844,6 +1896,7 @@ class VIEW3D_MT_edit_meta(bpy.types.Menu): layout.operator("ed.undo") layout.operator("ed.redo") + layout.operator("ed.undo_history") layout.separator() @@ -1951,7 +2004,7 @@ class VIEW3D_MT_edit_armature(bpy.types.Menu): layout.separator() - layout.operator_menu_enum("armature.flags_set", "mode", text="Bone Settings") + layout.menu("VIEW3D_MT_bone_options_toggle", text="Bone Settings") class VIEW3D_MT_armature_specials(bpy.types.Menu): @@ -2024,6 +2077,9 @@ class VIEW3D_PT_view3d_properties(bpy.types.Panel): elif not view.lock_object: col.prop(view, "lock_cursor", text="Lock to Cursor") + col = layout.column() + col.prop(view, "lock_camera") + col = layout.column(align=True) col.label(text="Clip:") col.prop(view, "clip_start", text="Start") @@ -2315,7 +2371,8 @@ class VIEW3D_PT_etch_a_ton(bpy.types.Panel): col.prop(toolsettings, "use_etch_autoname") col.prop(toolsettings, "etch_number") col.prop(toolsettings, "etch_side") - col.operator("sketch.convert", text="Convert") + + col.operator("sketch.convert", text="Convert") class VIEW3D_PT_context_properties(bpy.types.Panel): @@ -2348,7 +2405,7 @@ class VIEW3D_PT_context_properties(bpy.types.Panel): def draw(self, context): import rna_prop_ui - member = __class__._active_context_member(context) + member = VIEW3D_PT_context_properties._active_context_member(context) if member: # Draw with no edit button diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index a6db6fbdde8..91cfd22b3d6 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -58,8 +58,8 @@ def draw_gpencil_tools(context, layout): row = col.row() row.prop(context.tool_settings, "use_grease_pencil_sessions") -# ********** default tools for objectmode **************** +# ********** default tools for objectmode **************** class VIEW3D_PT_tools_objectmode(View3DPanel, bpy.types.Panel): bl_context = "objectmode" @@ -117,7 +117,8 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, bpy.types.Panel): col.operator("transform.translate") col.operator("transform.rotate") col.operator("transform.resize", text="Scale") - col.operator("transform.shrink_fatten", text="Along Normal") + col.operator("transform.shrink_fatten", text="Shrink/Fatten") + col.operator("transform.push_pull", text="Push/Pull") col = layout.column(align=True) col.label(text="Deform:") @@ -466,7 +467,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel): def draw(self, context): layout = self.layout - settings = __class__.paint_settings(context) + settings = self.paint_settings(context) brush = settings.brush if not context.particle_edit_object: @@ -687,7 +688,7 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel): def draw(self, context): layout = self.layout - settings = __class__.paint_settings(context) + settings = self.paint_settings(context) brush = settings.brush tex_slot = brush.texture_slot @@ -786,7 +787,7 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel, bpy.types.Panel): def draw(self, context): layout = self.layout - settings = __class__.paint_settings(context) + settings = self.paint_settings(context) brush = settings.brush col = layout.column(align=True) @@ -821,7 +822,7 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel): def draw(self, context): layout = self.layout - settings = __class__.paint_settings(context) + settings = self.paint_settings(context) brush = settings.brush image_paint = context.image_paint_object @@ -944,7 +945,6 @@ class VIEW3D_PT_sculpt_options(PaintPanel, bpy.types.Panel): tool_settings = context.tool_settings sculpt = tool_settings.sculpt - settings = __class__.paint_settings(context) layout.label(text="Lock:") row = layout.row(align=True) @@ -974,7 +974,6 @@ class VIEW3D_PT_sculpt_symmetry(PaintPanel, bpy.types.Panel): layout = self.layout sculpt = context.tool_settings.sculpt - settings = __class__.paint_settings(context) split = layout.split() @@ -997,14 +996,22 @@ class VIEW3D_PT_tools_brush_appearance(PaintPanel, bpy.types.Panel): @classmethod def poll(cls, context): - return (context.sculpt_object and context.tool_settings.sculpt) or (context.vertex_paint_object and context.tool_settings.vertex_paint) or (context.weight_paint_object and context.tool_settings.weight_paint) or (context.image_paint_object and context.tool_settings.image_paint) + ts = context.tool_settings + return ((context.sculpt_object and ts.sculpt) or + (context.vertex_paint_object and ts.vertex_paint) or + (context.weight_paint_object and ts.weight_paint) or + (context.image_paint_object and ts.image_paint)) def draw(self, context): layout = self.layout - settings = __class__.paint_settings(context) + settings = self.paint_settings(context) brush = settings.brush + if brush is None: # unlikely but can happen + layout.label(text="Brush Unset") + return + col = layout.column() if context.sculpt_object and context.tool_settings.sculpt: @@ -1245,7 +1252,7 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, bpy.types.Panel): if pe.type == 'PARTICLES': if ob.particle_systems: if len(ob.particle_systems) > 1: - layout.template_list(ob, "particle_systems", ob.particle_systems, "active_index", type='ICONS') + layout.template_list(ob, "particle_systems", ob.particle_systems, "active_index", rows=2, maxrows=3) ptcache = ob.particle_systems.active.point_cache else: @@ -1254,7 +1261,7 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, bpy.types.Panel): ptcache = md.point_cache if ptcache and len(ptcache.point_caches) > 1: - layout.template_list(ptcache, "point_caches", ptcache.point_caches, "active_index", type='ICONS') + layout.template_list(ptcache, "point_caches", ptcache.point_caches, "active_index", rows=2, maxrows=3) if not pe.is_editable: layout.label(text="Point cache must be baked") diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index 8cb63ea48cf..dcc1afed74b 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -407,7 +407,7 @@ class BUILTIN_KSI_DeltaRotation(bpy.types.KeyingSetInfo): # add the property name to the base path # rotation mode affects the property used if data.rotation_mode == 'QUATERNION': - path = path_add_property(base_path, "delta_rotation_quaternion") + path = keyingsets_utils.path_add_property(base_path, "delta_rotation_quaternion") elif data.rotation_mode == 'AXIS_ANGLE': # XXX: for now, this is not available yet #path = path_add_property(base_path, "delta_rotation_axis_angle") |