diff options
Diffstat (limited to 'release/scripts/startup/bl_operators')
21 files changed, 687 insertions, 138 deletions
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index e44fce63acd..35c7a55b6da 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -19,14 +19,16 @@ # <pep8 compliant> if "bpy" in locals(): - from imp import reload as _reload + from importlib import reload for val in _modules_loaded.values(): - _reload(val) + reload(val) + del reload _modules = [ "add_mesh_torus", "anim", "clip", "console", + "file", "image", "mask", "mesh", @@ -53,7 +55,7 @@ if bpy.app.build_options.freestyle: _modules.append("freestyle") __import__(name=__name__, fromlist=_modules) _namespace = globals() -_modules_loaded = {name: _namespace[name] for name in _modules if name != 'bpy'} +_modules_loaded = {name: _namespace[name] for name in _modules if name != "bpy"} del _namespace diff --git a/release/scripts/startup/bl_operators/add_mesh_torus.py b/release/scripts/startup/bl_operators/add_mesh_torus.py index 449a4cef1ef..82014c87be9 100644 --- a/release/scripts/startup/bl_operators/add_mesh_torus.py +++ b/release/scripts/startup/bl_operators/add_mesh_torus.py @@ -20,9 +20,10 @@ import bpy from bpy.types import Operator -from bpy.props import (FloatProperty, - IntProperty, - ) +from bpy.props import ( + FloatProperty, + IntProperty, + ) from bpy.app.translations import pgettext_data as data_ from bpy_extras import object_utils @@ -152,40 +153,40 @@ class AddTorus(Operator, object_utils.AddObjectHelper): col = layout.column(align=True) col.label(text="Location") - col.prop(self, 'location', text="") + col.prop(self, "location", text="") col = layout.column(align=True) col.label(text="Rotation") - col.prop(self, 'rotation', text="") + col.prop(self, "rotation", text="") col = layout.column(align=True) col.label(text="Major Segments") - col.prop(self, 'major_segments', text="") + col.prop(self, "major_segments", text="") col = layout.column(align=True) col.label(text="Minor Segments") - col.prop(self, 'minor_segments', text="") + col.prop(self, "minor_segments", text="") col = layout.column(align=True) col.label(text="Torus Dimensions") - col.row().prop(self, 'mode', expand=True) + col.row().prop(self, "mode", expand=True) if self.mode == 'MAJOR_MINOR': col = layout.column(align=True) col.label(text="Major Radius") - col.prop(self, 'major_radius', text="") + col.prop(self, "major_radius", text="") col = layout.column(align=True) col.label(text="Minor Radius") - col.prop(self, 'minor_radius', text="") + col.prop(self, "minor_radius", text="") else: col = layout.column(align=True) col.label(text="Exterior Radius") - col.prop(self, 'abso_major_rad', text="") + col.prop(self, "abso_major_rad", text="") col = layout.column(align=True) col.label(text="Interior Radius") - col.prop(self, 'abso_minor_rad', text="") + col.prop(self, "abso_minor_rad", text="") def invoke(self, context, event): object_utils.object_add_grid_scale_apply_operator(self, context) diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index 756b75b8d03..f3575f26890 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -19,18 +19,20 @@ # <pep8-80 compliant> if "bpy" in locals(): - import imp + from importlib import reload if "anim_utils" in locals(): - imp.reload(anim_utils) + reload(anim_utils) + del reload import bpy from bpy.types import Operator -from bpy.props import (IntProperty, - BoolProperty, - EnumProperty, - StringProperty, - ) +from bpy.props import ( + IntProperty, + BoolProperty, + EnumProperty, + StringProperty, + ) class ANIM_OT_keying_set_export(Operator): @@ -83,7 +85,9 @@ class ANIM_OT_keying_set_export(Operator): f.write("ks.is_path_absolute = False\n") f.write("\n") - f.write("ks.bl_options = %r\n" % ks.bl_options) + f.write("ks.use_insertkey_needed = %s\n" % ks.use_insertkey_needed) + f.write("ks.use_insertkey_visual = %s\n" % ks.use_insertkey_visual) + f.write("ks.use_insertkey_xyz_to_rgb = %s\n" % ks.use_insertkey_xyz_to_rgb) f.write("\n") # -------------------------------------------------------- @@ -206,6 +210,12 @@ class BakeAction(Operator): description="Bake animation onto the object then clear parents (objects only)", default=False, ) + use_current_action = BoolProperty( + name="Overwrite Current Action", + description="Bake animation into current action, instead of creating a new one " + "(useful for baking only part of bones in an armature)", + default=False, + ) bake_types = EnumProperty( name="Bake Data", description="Which data's transformations to bake", @@ -220,6 +230,12 @@ class BakeAction(Operator): from bpy_extras import anim_utils + action = None + if self.use_current_action: + obj = context.object + if obj.animation_data: + action = obj.animation_data.action + action = anim_utils.bake_action(self.frame_start, self.frame_end, frame_step=self.step, @@ -230,6 +246,7 @@ class BakeAction(Operator): do_constraint_clear=self.clear_constraints, do_parents_clear=self.clear_parents, do_clean=True, + action=action, ) if action is None: diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py index 4ce300ecce2..0c77ea2ab7e 100644 --- a/release/scripts/startup/bl_operators/clip.py +++ b/release/scripts/startup/bl_operators/clip.py @@ -20,7 +20,7 @@ import bpy import os from bpy.types import Operator - +from bpy.props import FloatProperty from mathutils import Vector, Matrix @@ -124,6 +124,100 @@ def CLIP_default_settings_from_track(clip, track, framenr): settings.default_weight = track.weight +class CLIP_OT_filter_tracks(bpy.types.Operator): + """Filter tracks which has weirdly looking spikes in motion curves""" + bl_label = "Filter Tracks" + bl_idname = "clip.filter_tracks" + bl_options = {'UNDO', 'REGISTER'} + + track_threshold = FloatProperty( + name="Track Threshold", + description="Filter Threshold to select problematic tracks", + default=5.0, + ) + + @staticmethod + def _filter_values(context, threshold): + + def get_marker_coordinates_in_pixels(clip_size, track, frame_number): + marker = track.markers.find_frame(frame_number) + return Vector((marker.co[0] * clip_size[0], marker.co[1] * clip_size[1])) + + def marker_velocity(clip_size, track, frame): + marker_a = get_marker_coordinates_in_pixels(clip_size, track, frame) + marker_b = get_marker_coordinates_in_pixels(clip_size, track, frame - 1) + return marker_a - marker_b + + scene = context.scene + frame_start = scene.frame_start + frame_end = scene.frame_end + clip = context.space_data.clip + clip_size = clip.size[:] + + bpy.ops.clip.clean_tracks(frames=10, action='DELETE_TRACK') + + tracks_to_clean = set() + + for frame in range(frame_start, frame_end + 1): + + # Find tracks with markers in both this frame and the previous one. + relevant_tracks = [ + track for track in clip.tracking.tracks + if (track.markers.find_frame(frame) and + track.markers.find_frame(frame - 1))] + + if not relevant_tracks: + continue + + # Get average velocity and deselect track. + average_velocity = Vector((0.0, 0.0)) + for track in relevant_tracks: + track.select = False + average_velocity += marker_velocity(clip_size, track, frame) + if len(relevant_tracks) >= 1: + average_velocity = average_velocity / len(relevant_tracks) + + # Then find all markers that behave differently than the average. + for track in relevant_tracks: + track_velocity = marker_velocity(clip_size, track, frame) + distance = (average_velocity - track_velocity).length + + if distance > threshold: + tracks_to_clean.add(track) + + for track in tracks_to_clean: + track.select = True + return len(tracks_to_clean) + + @classmethod + def poll(cls, context): + space = context.space_data + return (space.type == 'CLIP_EDITOR') and space.clip + + def execute(self, context): + num_tracks = self._filter_values(context, self.track_threshold) + self.report({'INFO'}, "Identified %d problematic tracks" % num_tracks) + return {'FINISHED'} + + +class CLIP_OT_set_active_clip(bpy.types.Operator): + bl_label = "Set Active Clip" + bl_idname = "clip.set_active_clip" + + @classmethod + def poll(cls, context): + space = context.space_data + return space.type == 'CLIP_EDITOR' + + def execute(self, context): + clip = context.space_data.clip + scene = context.scene + scene.active_clip = clip + scene.render.resolution_x = clip.size[0] + scene.render.resolution_y = clip.size[1] + return {'FINISHED'} + + class CLIP_OT_track_to_empty(Operator): """Create an Empty object which will be copying movement of active track""" @@ -131,7 +225,8 @@ class CLIP_OT_track_to_empty(Operator): bl_label = "Link Empty to Track" bl_options = {'UNDO', 'REGISTER'} - def _link_track(self, context, clip, tracking_object, track): + @staticmethod + def _link_track(context, clip, tracking_object, track): sc = context.space_data constraint = None ob = None @@ -237,7 +332,8 @@ class CLIP_OT_delete_proxy(Operator): return wm.invoke_confirm(self, event) - def _rmproxy(self, abspath): + @staticmethod + def _rmproxy(abspath): import shutil if not os.path.exists(abspath): @@ -458,8 +554,8 @@ class CLIP_OT_setup_tracking_scene(Operator): world.light_settings.sample_method = 'ADAPTIVE_QMC' world.light_settings.samples = 7 world.light_settings.threshold = 0.005 - if hasattr(scene, 'cycles'): - world.light_settings.ao_factor = 0.05 + if hasattr(scene, "cycles"): + world.light_settings.ao_factor = 0.05 @staticmethod def _findOrCreateCamera(context): @@ -747,7 +843,7 @@ class CLIP_OT_setup_tracking_scene(Operator): self._offsetNodes(tree) scene.render.alpha_mode = 'TRANSPARENT' - if hasattr(scene, 'cycles'): + if hasattr(scene, "cycles"): scene.cycles.film_transparent = True @staticmethod @@ -920,3 +1016,58 @@ class CLIP_OT_track_settings_as_default(Operator): CLIP_default_settings_from_track(clip, track, framenr) return {'FINISHED'} + + +class CLIP_OT_track_settings_to_track(bpy.types.Operator): + """Copy tracking settings from active track to selected tracks""" + + bl_label = "Copy Track Settings" + bl_idname = "clip.track_settings_to_track" + bl_options = {'UNDO', 'REGISTER'} + + _attrs_track = ( + "correlation_min", + "frames_limit", + "pattern_match", + "margin", + "motion_model", + "use_brute", + "use_normalization", + "use_mask", + "use_red_channel", + "use_green_channel", + "use_blue_channel", + "weight" + ) + + _attrs_marker = ( + "pattern_corners", + "search_min", + "search_max", + ) + + @classmethod + def poll(cls, context): + space = context.space_data + if space.type != 'CLIP_EDITOR': + return False + clip = space.clip + return clip and clip.tracking.tracks.active + + def execute(self, context): + space = context.space_data + clip = space.clip + track = clip.tracking.tracks.active + + framenr = context.scene.frame_current - clip.frame_start + 1 + marker = track.markers.find_frame(framenr, False) + + for t in clip.tracking.tracks: + if t.select and t != track: + marker_selected = t.markers.find_frame(framenr, False) + for attr in self._attrs_track: + setattr(t, attr, getattr(track, attr)) + for attr in self._attrs_marker: + setattr(marker_selected, attr, getattr(marker, attr)) + + return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/console.py b/release/scripts/startup/bl_operators/console.py index 4e99acd4f36..8cfc977294a 100644 --- a/release/scripts/startup/bl_operators/console.py +++ b/release/scripts/startup/bl_operators/console.py @@ -20,9 +20,10 @@ import bpy from bpy.types import Operator -from bpy.props import (BoolProperty, - StringProperty, - ) +from bpy.props import ( + BoolProperty, + StringProperty, + ) def _lang_module_get(sc): diff --git a/release/scripts/startup/bl_operators/file.py b/release/scripts/startup/bl_operators/file.py new file mode 100644 index 00000000000..efcc7d5c65e --- /dev/null +++ b/release/scripts/startup/bl_operators/file.py @@ -0,0 +1,244 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from bpy.types import Operator +from bpy.props import ( + StringProperty, + BoolProperty, + CollectionProperty, + ) + +# ########## Datablock previews... ########## + + +class WM_OT_previews_batch_generate(Operator): + """Generate selected .blend file's previews""" + bl_idname = "wm.previews_batch_generate" + bl_label = "Batch-Generate Previews" + bl_options = {'REGISTER'} + + # ----------- + # File props. + files = CollectionProperty( + type=bpy.types.OperatorFileListElement, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + directory = StringProperty( + maxlen=1024, + subtype='FILE_PATH', + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # Show only images/videos, and directories! + filter_blender = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + filter_folder = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # ----------- + # Own props. + use_scenes = BoolProperty( + default=True, + name="Scenes", + description="Generate scenes' previews", + ) + use_groups = BoolProperty( + default=True, + name="Groups", + description="Generate groups' previews", + ) + use_objects = BoolProperty( + default=True, + name="Objects", + description="Generate objects' previews", + ) + use_intern_data = BoolProperty( + default=True, + name="Mat/Tex/...", + description="Generate 'internal' previews (materials, textures, images, etc.)", + ) + + use_trusted = BoolProperty( + default=False, + name="Trusted Blend Files", + description="Enable python evaluation for selected files", + ) + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + if "subprocess" in locals(): + import imp + imp.reload(preview_render) + else: + import os + import subprocess + from bl_previews_utils import bl_previews_render as preview_render + + context.window_manager.progress_begin(0, len(self.files)) + context.window_manager.progress_update(0) + for i, fn in enumerate(self.files): + blen_path = os.path.join(self.directory, fn.name) + cmd = [ + bpy.app.binary_path, + "--background", + "--factory-startup", + "-noaudio", + ] + if self.use_trusted: + cmd.append("--enable-autoexec") + cmd.extend([ + blen_path, + "--python", + os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"), + "--", + ]) + if not self.use_scenes: + cmd.append('--no_scenes') + if not self.use_groups: + cmd.append('--no_groups') + if not self.use_objects: + cmd.append('--no_objects') + if not self.use_intern_data: + cmd.append('--no_data_intern') + if subprocess.call(cmd): + self.report({'ERROR'}, "Previews generation process failed for file '%s'!" % blen_path) + context.window_manager.progress_end() + return {'CANCELLED'} + context.window_manager.progress_update(i + 1) + context.window_manager.progress_end() + + return {'FINISHED'} + + +class WM_OT_previews_batch_clear(Operator): + """Clear selected .blend file's previews""" + bl_idname = "wm.previews_batch_clear" + bl_label = "Batch-Clear Previews" + bl_options = {'REGISTER'} + + # ----------- + # File props. + files = CollectionProperty( + type=bpy.types.OperatorFileListElement, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + directory = StringProperty( + maxlen=1024, + subtype='FILE_PATH', + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # Show only images/videos, and directories! + filter_blender = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + filter_folder = BoolProperty( + default=True, + options={'HIDDEN', 'SKIP_SAVE'}, + ) + + # ----------- + # Own props. + use_scenes = BoolProperty( + default=True, + name="Scenes", + description="Clear scenes' previews", + ) + use_groups = BoolProperty(default=True, + name="Groups", + description="Clear groups' previews", + ) + use_objects = BoolProperty( + default=True, + name="Objects", + description="Clear objects' previews", + ) + use_intern_data = BoolProperty( + default=True, + name="Mat/Tex/...", + description="Clear 'internal' previews (materials, textures, images, etc.)", + ) + + use_trusted = BoolProperty( + default=False, + name="Trusted Blend Files", + description="Enable python evaluation for selected files", + ) + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + if "subprocess" in locals(): + import imp + imp.reload(preview_render) + else: + import os + import subprocess + from bl_previews_utils import bl_previews_render as preview_render + + context.window_manager.progress_begin(0, len(self.files)) + context.window_manager.progress_update(0) + for i, fn in enumerate(self.files): + blen_path = os.path.join(self.directory, fn.name) + cmd = [ + bpy.app.binary_path, + "--background", + "--factory-startup", + "-noaudio", + ] + if self.use_trusted: + cmd.append("--enable-autoexec") + cmd.extend([ + blen_path, + "--python", + os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"), + "--", + "--clear", + ]) + if not self.use_scenes: + cmd.append('--no_scenes') + if not self.use_groups: + cmd.append('--no_groups') + if not self.use_objects: + cmd.append('--no_objects') + if not self.use_intern_data: + cmd.append('--no_data_intern') + if subprocess.call(cmd): + self.report({'ERROR'}, "Previews clear process failed for file '%s'!" % blen_path) + context.window_manager.progress_end() + return {'CANCELLED'} + context.window_manager.progress_update(i + 1) + context.window_manager.progress_end() + + return {'FINISHED'} + diff --git a/release/scripts/startup/bl_operators/freestyle.py b/release/scripts/startup/bl_operators/freestyle.py index 453be519abf..edda92284d2 100644 --- a/release/scripts/startup/bl_operators/freestyle.py +++ b/release/scripts/startup/bl_operators/freestyle.py @@ -16,10 +16,13 @@ # # ##### END GPL LICENSE BLOCK ##### -import sys import bpy -from bpy.props import (BoolProperty, EnumProperty, StringProperty) +from bpy.props import ( + BoolProperty, + EnumProperty, + StringProperty, + ) class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator): @@ -29,11 +32,16 @@ class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator): bl_label = "Fill Range by Selection" bl_options = {'INTERNAL'} - type = EnumProperty(name="Type", description="Type of the modifier to work on", - items=(("COLOR", "Color", "Color modifier type"), - ("ALPHA", "Alpha", "Alpha modifier type"), - ("THICKNESS", "Thickness", "Thickness modifier type"))) - name = StringProperty(name="Name", description="Name of the modifier to work on") + type = EnumProperty( + name="Type", description="Type of the modifier to work on", + items=(("COLOR", "Color", "Color modifier type"), + ("ALPHA", "Alpha", "Alpha modifier type"), + ("THICKNESS", "Thickness", "Thickness modifier type")), + ) + name = StringProperty( + name="Name", + description="Name of the modifier to work on", + ) @classmethod def poll(cls, context): @@ -41,6 +49,8 @@ class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator): return rl and rl.freestyle_settings.linesets.active def execute(self, context): + import sys + scene = context.scene rl = scene.render.layers.active lineset = rl.freestyle_settings.linesets.active diff --git a/release/scripts/startup/bl_operators/image.py b/release/scripts/startup/bl_operators/image.py index 1653459bd71..f00f5d97c5e 100644 --- a/release/scripts/startup/bl_operators/image.py +++ b/release/scripts/startup/bl_operators/image.py @@ -33,7 +33,8 @@ class EditExternally(Operator): subtype='FILE_PATH', ) - def _editor_guess(self, context): + @staticmethod + def _editor_guess(context): import sys image_editor = context.user_preferences.filepaths.image_editor diff --git a/release/scripts/startup/bl_operators/mask.py b/release/scripts/startup/bl_operators/mask.py index 60208d27338..aa984659430 100644 --- a/release/scripts/startup/bl_operators/mask.py +++ b/release/scripts/startup/bl_operators/mask.py @@ -18,7 +18,6 @@ # <pep8-80 compliant> -import bpy from bpy.types import Menu diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py index f86c31cd9cc..ea504d48448 100644 --- a/release/scripts/startup/bl_operators/mesh.py +++ b/release/scripts/startup/bl_operators/mesh.py @@ -75,7 +75,6 @@ class MeshMirrorUV(Operator): double_warn += co in mirror_lt mirror_lt[co] = i - #for i, v in enumerate(mesh.vertices): vmap = {} for mirror_a, mirror_b in ((mirror_gt, mirror_lt), (mirror_lt, mirror_gt)): diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index 9bdd9289700..d6aeab5c02b 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -20,15 +20,17 @@ import bpy import nodeitems_utils -from bpy.types import (Operator, - PropertyGroup, - ) -from bpy.props import (BoolProperty, - CollectionProperty, - EnumProperty, - IntProperty, - StringProperty, - ) +from bpy.types import ( + Operator, + PropertyGroup, + ) +from bpy.props import ( + BoolProperty, + CollectionProperty, + EnumProperty, + IntProperty, + StringProperty, + ) class NodeSetting(PropertyGroup): @@ -40,7 +42,7 @@ class NodeSetting(PropertyGroup): # Base class for node 'Add' operators -class NodeAddOperator(): +class NodeAddOperator: type = StringProperty( name="Node Type", @@ -122,8 +124,8 @@ class NodeAddOperator(): result = self.execute(context) if self.use_transform and ('FINISHED' in result): - # removes the node again if transform is cancelled - bpy.ops.transform.translate('INVOKE_DEFAULT', remove_on_cancel=True) + # removes the node again if transform is canceled + bpy.ops.node.translate_attach_remove_on_cancel('INVOKE_DEFAULT') return result @@ -218,7 +220,7 @@ class NODE_OT_add_search(NodeAddOperator, Operator): self.create_node(context, item.nodetype) if self.use_transform: - bpy.ops.transform.translate('INVOKE_DEFAULT', remove_on_cancel=True) + bpy.ops.node.translate_attach_remove_on_cancel('INVOKE_DEFAULT') return {'FINISHED'} else: diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index c1f75c74bb4..b89890a223c 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -20,11 +20,13 @@ import bpy from bpy.types import Operator -from bpy.props import (StringProperty, - BoolProperty, - EnumProperty, - IntProperty, - FloatProperty) +from bpy.props import ( + StringProperty, + BoolProperty, + EnumProperty, + IntProperty, + FloatProperty, + ) class SelectPattern(Operator): @@ -250,7 +252,7 @@ class SubdivisionSet(Operator): if not relative: if level > mod.total_levels: sub = level - mod.total_levels - for i in range (0, sub): + for i in range(sub): bpy.ops.object.multires_subdivide(modifier="Multires") if obj.mode == 'SCULPT': @@ -577,7 +579,8 @@ class MakeDupliFace(Operator): bl_label = "Make Dupli-Face" bl_options = {'REGISTER', 'UNDO'} - def _main(self, context): + @staticmethod + def _main(context): from mathutils import Vector SCALE_FAC = 0.01 @@ -641,6 +644,9 @@ class MakeDupliFace(Operator): ob_new.use_dupli_faces_scale = True ob_new.dupli_faces_scale = 1.0 / SCALE_FAC + ob_inst.select = True + ob_new.select = True + def execute(self, context): self._main(context) return {'FINISHED'} @@ -922,7 +928,7 @@ class LodGenerate(Operator): lod.location.y = ob.location.y + 3.0 * i if i == 1: - modifier = lod.modifiers.new("lod_decimate", "DECIMATE") + modifier = lod.modifiers.new("lod_decimate", 'DECIMATE') else: modifier = lod.modifiers[-1] diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py index e843209da3c..3c84e5dc553 100644 --- a/release/scripts/startup/bl_operators/object_align.py +++ b/release/scripts/startup/bl_operators/object_align.py @@ -70,7 +70,8 @@ def GlobalBB_HQ(obj): # Initialize the variables with the last vertex - verts = obj.data.vertices + me = obj.to_mesh(scene=bpy.context.scene, apply_modifiers=True, settings='PREVIEW') + verts = me.vertices val = matrix_world * verts[-1].co @@ -111,6 +112,8 @@ def GlobalBB_HQ(obj): if val > up: up = val + bpy.data.meshes.remove(me) + return Vector((left, front, up)), Vector((right, back, down)) @@ -338,7 +341,10 @@ def align_objects(context, return True -from bpy.props import EnumProperty, BoolProperty +from bpy.props import ( + EnumProperty, + BoolProperty + ) class AlignObjects(Operator): diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py index 06dc82d2b77..414855c7e35 100644 --- a/release/scripts/startup/bl_operators/object_quick_effects.py +++ b/release/scripts/startup/bl_operators/object_quick_effects.py @@ -21,12 +21,13 @@ from mathutils import Vector import bpy from bpy.types import Operator -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): @@ -74,7 +75,7 @@ class QuickFur(Operator): def execute(self, context): fake_context = context.copy() mesh_objects = [obj for obj in context.selected_objects - if obj.type == 'MESH'] + if obj.type == 'MESH' and obj.mode == 'OBJECT'] if not mesh_objects: self.report({'ERROR'}, "Select at least one mesh object") @@ -387,7 +388,7 @@ class QuickSmoke(Operator): links.new(node_add_shader_1.outputs["Shader"], node_out.inputs["Volume"]) - if self.style in {'SMOKE', 'BOTH'}: + if self.style in {'SMOKE', 'FIRE', 'BOTH'}: # Smoke # Add shader 2 diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index f89792bea6e..63c1945d2d2 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -23,7 +23,7 @@ from bpy.types import Menu, Operator from bpy.props import StringProperty, BoolProperty -class AddPresetBase(): +class AddPresetBase: """Base preset class, only for subclassing subclasses must define - preset_values @@ -45,11 +45,28 @@ class AddPresetBase(): options={'HIDDEN', 'SKIP_SAVE'}, ) + # needed for mix-ins + order = [ + "name", + "remove_active", + ] + @staticmethod def as_filename(name): # could reuse for other presets - for char in " !@#$%^&*(){}:\";'[]<>,.\\/?": - name = name.replace(char, '_') - return name.lower().strip() + + # lazy init maketrans + def maketrans_init(): + cls = AddPresetBase + attr = "_as_filename_trans" + + trans = getattr(cls, attr, None) + if trans is None: + trans = str.maketrans({char: "_" for char in " !@#$%^&*(){}:\";'[]<>,.\\/?"}) + setattr(cls, attr, trans) + return trans + + trans = maketrans_init() + return name.lower().strip().translate(trans) def execute(self, context): import os @@ -277,6 +294,26 @@ class AddPresetCamera(AddPresetBase, Operator): return preset_values +class AddPresetSafeAreas(AddPresetBase, Operator): + """Add or remove a Safe Areas Preset""" + bl_idname = "safe_areas.preset_add" + bl_label = "Add Safe Area Preset" + preset_menu = "SAFE_AREAS_MT_presets" + + preset_defines = [ + "safe_areas = bpy.context.scene.safe_areas" + ] + + preset_values = [ + "safe_areas.title", + "safe_areas.action", + "safe_areas.title_center", + "safe_areas.action_center", + ] + + preset_subdir = "safe_areas" + + class AddPresetSSS(AddPresetBase, Operator): """Add or remove a Subsurface Scattering Preset""" bl_idname = "material.sss_preset_add" @@ -345,6 +382,36 @@ class AddPresetFluid(AddPresetBase, Operator): preset_subdir = "fluid" +class AddPresetHairDynamics(AddPresetBase, Operator): + """Add or remove a Hair Dynamics Preset""" + bl_idname = "particle.hair_dynamics_preset_add" + bl_label = "Add Hair Dynamics Preset" + preset_menu = "PARTICLE_MT_hair_dynamics_presets" + + preset_defines = [ + "psys = bpy.context.particle_system", + "cloth = bpy.context.particle_system.cloth", + "settings = bpy.context.particle_system.cloth.settings", + "collision = bpy.context.particle_system.cloth.collision_settings", + ] + + preset_subdir = "hair_dynamics" + + preset_values = [ + "settings.quality", + "settings.mass", + "settings.bending_stiffness", + "psys.settings.bending_random", + "settings.bending_damping", + "settings.air_damping", + "settings.internal_friction", + "settings.density_target", + "settings.density_strength", + "settings.voxel_cell_size", + "settings.pin_stiffness", + ] + + class AddPresetSunSky(AddPresetBase, Operator): """Add or remove a Sky & Atmosphere Preset""" bl_idname = "lamp.sunsky_preset_add" diff --git a/release/scripts/startup/bl_operators/rigidbody.py b/release/scripts/startup/bl_operators/rigidbody.py index 9a3aae53ceb..237c2d55672 100644 --- a/release/scripts/startup/bl_operators/rigidbody.py +++ b/release/scripts/startup/bl_operators/rigidbody.py @@ -64,17 +64,19 @@ class CopyRigidbodySettings(Operator): for o in context.selected_objects: if o.type != 'MESH': o.select = False + elif o.rigid_body is None: + # Add rigidbody to object! + scene.objects.active = o + bpy.ops.rigidbody.object_add() + scene.objects.active = obj_act objects = context.selected_objects if objects: - # add selected objects to active one groups and recalculate - bpy.ops.group.objects_add_active() - scene.frame_set(scene.frame_current) rb_from = obj_act.rigid_body # copy settings for o in objects: rb_to = o.rigid_body - if (o == obj_act) or (rb_to is None): + if o == obj_act: continue for attr in self._attrs: setattr(rb_to, attr, getattr(rb_from, attr)) 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 3a7a9b99cde..a5565699364 100644 --- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py +++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py @@ -107,11 +107,20 @@ class PlayRenderedAnim(Operator): del file_a, file_b, frame_tmp file = bpy.path.abspath(file) # expand '//' else: + path_valid = True # works for movies and images - file = rd.frame_path(frame=scene.frame_start) + file = rd.frame_path(frame=scene.frame_start, preview=scene.use_preview_range) file = bpy.path.abspath(file) # expand '//' if not os.path.exists(file): self.report({'WARNING'}, "File %r not found" % file) + path_valid = False + + #one last try for full range if we used preview range + if scene.use_preview_range and not path_valid: + file = rd.frame_path(frame=scene.frame_start, preview=False) + file = bpy.path.abspath(file) # expand '//' + if not os.path.exists(file): + self.report({'WARNING'}, "File %r not found" % file) cmd = [player_path] # extra options, fps controls etc. diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 8f618e0632e..a120e2b2461 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -23,7 +23,7 @@ from bpy.types import Operator import mathutils -class prettyface(object): +class prettyface: __slots__ = ( "uv", "width", diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py index aa8a1742c1e..73e6bcd9b0c 100644 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py @@ -670,7 +670,7 @@ def VectoQuat(vec): return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted() -class thickface(object): +class thickface: __slost__= "v", "uv", "no", "area", "edge_keys" def __init__(self, face, uv_layer, mesh_verts): self.v = [mesh_verts[i] for i in face.vertices] @@ -708,6 +708,7 @@ def main(context, island_margin, projection_limit, user_area_weight, + use_aspect ): global USER_FILL_HOLES global USER_FILL_HOLES_QUALITY @@ -720,7 +721,6 @@ def main(context, global dict_matrix dict_matrix = {} - # Constants: # Takes a list of faces that make up a UV island and rotate # until they optimally fit inside a square. @@ -992,9 +992,31 @@ def main(context, print("Smart Projection time: %.2f" % (time.time() - time1)) # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec" % (time.time() - time1)) + # aspect correction is only done in edit mode - and only smart unwrap supports currently if is_editmode: bpy.ops.object.mode_set(mode='EDIT') + if use_aspect: + import bmesh + aspect = context.scene.uvedit_aspect(context.active_object) + if aspect[0] > aspect[1]: + aspect[0] = aspect[1]/aspect[0]; + aspect[1] = 1.0 + else: + aspect[1] = aspect[0]/aspect[1]; + aspect[0] = 1.0 + + bm = bmesh.from_edit_mesh(me) + + uv_act = bm.loops.layers.uv.active + + faces = [f for f in bm.faces if f.select] + + for f in faces: + for l in f.loops: + l[uv_act].uv[0] *= aspect[0] + l[uv_act].uv[1] *= aspect[1] + dict_matrix.clear() #XXX Window.DrawProgressBar(1.0, "") @@ -1017,7 +1039,7 @@ def main(context, ] """ -from bpy.props import FloatProperty +from bpy.props import FloatProperty, BoolProperty class SmartProject(Operator): @@ -1046,6 +1068,11 @@ class SmartProject(Operator): min=0.0, max=1.0, default=0.0, ) + use_aspect = BoolProperty( + name="Correct Aspect", + description="Map UVs taking image aspect ratio into account", + default=True + ) @classmethod def poll(cls, context): @@ -1056,6 +1083,7 @@ class SmartProject(Operator): self.island_margin, self.angle_limit, self.user_area_weight, + self.use_aspect ) return {'FINISHED'} diff --git a/release/scripts/startup/bl_operators/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py index ecb1ecf7f47..892e1822d68 100644 --- a/release/scripts/startup/bl_operators/vertexpaint_dirt.py +++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py @@ -27,11 +27,9 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only): from mathutils import Vector from math import acos + import array - vert_tone = [0.0] * len(me.vertices) - - min_tone = 180.0 - max_tone = 0.0 + vert_tone = array.array("f", [0.0]) * len(me.vertices) # create lookup table for each vertex's connected vertices (via edges) con = [] @@ -74,7 +72,7 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, # blur tones for i in range(blur_iterations): # backup the original tones - orig_vert_tone = list(vert_tone) + orig_vert_tone = vert_tone[:] # use connected verts look up for blurring for j, c in enumerate(con): @@ -82,20 +80,18 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, vert_tone[j] += blur_strength * orig_vert_tone[v] vert_tone[j] /= len(c) * blur_strength + 1 + del orig_vert_tone min_tone = min(vert_tone) max_tone = max(vert_tone) - # debug information - # print(min_tone * 2 * math.pi) - # print(max_tone * 2 * math.pi) - # print(clamp_clean) - # print(clamp_dirt) - tone_range = max_tone - min_tone - if not tone_range: - return {'CANCELLED'} + if tone_range < 0.0001: + # weak, don't cancel, see T43345 + tone_range = 0.0 + else: + tone_range = 1.0 / tone_range active_col_layer = None @@ -112,7 +108,6 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, return {'CANCELLED'} use_paint_mask = me.use_paint_mask - for i, p in enumerate(me.polygons): if not use_paint_mask or p.select: for loop_index in p.loop_indices: @@ -120,11 +115,10 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, v = loop.vertex_index col = active_col_layer[loop_index].color tone = vert_tone[v] - tone = (tone - min_tone) / tone_range + tone = (tone - min_tone) * tone_range if dirt_only: - tone = min(tone, 0.5) - tone *= 2.0 + tone = min(tone, 0.5) * 2.0 col[0] = tone * col[0] col[1] = tone * col[1] @@ -182,15 +176,9 @@ class VertexPaintDirt(Operator): return (obj and obj.type == 'MESH') def execute(self, context): - import time - obj = context.object mesh = obj.data - t = time.time() - ret = applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, self.dirt_angle, self.clean_angle, self.dirt_only) - print('Dirt calculated in %.6f' % (time.time() - t)) - return ret diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 8d04cb132e6..c228e33965f 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -20,12 +20,13 @@ import bpy from bpy.types import Operator -from bpy.props import (StringProperty, - BoolProperty, - IntProperty, - FloatProperty, - EnumProperty, - ) +from bpy.props import ( + StringProperty, + BoolProperty, + IntProperty, + FloatProperty, + EnumProperty, + ) from bpy.app.translations import pgettext_tip as tip_ @@ -718,7 +719,7 @@ class WM_OT_context_modal_mouse(Operator): """Adjust arbitrary values with mouse input""" bl_idname = "wm.context_modal_mouse" bl_label = "Context Modal Mouse" - bl_options = {'GRAB_POINTER', 'BLOCKING', 'UNDO', 'INTERNAL'} + bl_options = {'GRAB_CURSOR', 'BLOCKING', 'UNDO', 'INTERNAL'} data_path_iter = data_path_iter data_path_item = data_path_item @@ -973,10 +974,12 @@ class WM_OT_doc_view_manual(Operator): url = self._lookup_rna_url(rna_id) if url is None: - self.report({'WARNING'}, "No reference available %r, " - "Update info in 'rna_wiki_reference.py' " - " or callback to bpy.utils.manual_map()" % - self.doc_id) + self.report( + {'WARNING'}, + "No reference available %r, " + "Update info in 'rna_manual_reference.py' " + "or callback to bpy.utils.manual_map()" % + self.doc_id) return {'CANCELLED'} else: import webbrowser @@ -1129,7 +1132,11 @@ class WM_OT_properties_edit(Operator): ) def execute(self, context): - from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear + from rna_prop_ui import ( + rna_idprop_ui_prop_get, + rna_idprop_ui_prop_clear, + rna_idprop_ui_prop_update, + ) data_path = self.data_path value = self.value @@ -1161,6 +1168,9 @@ class WM_OT_properties_edit(Operator): exec_str = "item[%r] = %s" % (prop, repr(value_eval)) # print(exec_str) exec(exec_str) + + rna_idprop_ui_prop_update(item, prop) + self._last_prop[:] = [prop] prop_type = type(item[prop]) @@ -1242,7 +1252,10 @@ class WM_OT_properties_add(Operator): data_path = rna_path def execute(self, context): - from rna_prop_ui import rna_idprop_ui_prop_get + from rna_prop_ui import ( + rna_idprop_ui_prop_get, + rna_idprop_ui_prop_update, + ) data_path = self.data_path item = eval("context.%s" % data_path) @@ -1260,6 +1273,7 @@ class WM_OT_properties_add(Operator): prop = unique_name(item.keys()) item[prop] = 1.0 + rna_idprop_ui_prop_update(item, prop) # not essential, but without this we get [#31661] prop_ui = rna_idprop_ui_prop_get(item, prop) @@ -1295,9 +1309,17 @@ class WM_OT_properties_remove(Operator): property = rna_property def execute(self, context): + from rna_prop_ui import ( + rna_idprop_ui_prop_clear, + rna_idprop_ui_prop_update, + ) data_path = self.data_path item = eval("context.%s" % data_path) - del item[self.property] + prop = self.property + rna_idprop_ui_prop_update(item, prop) + del item[prop] + rna_idprop_ui_prop_clear(item, prop) + return {'FINISHED'} @@ -1737,7 +1759,7 @@ class WM_OT_addon_enable(Operator): err_str = traceback.format_exc() print(err_str) - mod = addon_utils.enable(self.module, handle_error=err_cb) + mod = addon_utils.enable(self.module, default_set=True, handle_error=err_cb) if mod: info = addon_utils.module_bl_info(mod) @@ -1781,7 +1803,7 @@ class WM_OT_addon_disable(Operator): err_str = traceback.format_exc() print(err_str) - addon_utils.disable(self.module, handle_error=err_cb) + addon_utils.disable(self.module, default_set=True, handle_error=err_cb) if err_str: self.report({'ERROR'}, err_str) @@ -1987,7 +2009,6 @@ class WM_OT_addon_install(Operator): # if not compressed file just copy into the addon path try: shutil.copyfile(pyfile, path_dest) - except: traceback.print_exc() return {'CANCELLED'} @@ -1998,7 +2019,7 @@ class WM_OT_addon_install(Operator): # disable any addons we may have enabled previously and removed. # this is unlikely but do just in case. bug [#23978] for new_addon in addons_new: - addon_utils.disable(new_addon) + addon_utils.disable(new_addon, default_set=True) # possible the zip contains multiple addons, we could disallow this # but for now just use the first @@ -2062,7 +2083,7 @@ class WM_OT_addon_remove(Operator): return {'CANCELLED'} # in case its enabled - addon_utils.disable(self.module) + addon_utils.disable(self.module, default_set=True) import shutil if isdir: @@ -2102,15 +2123,9 @@ class WM_OT_addon_expand(Operator): module_name = self.module - # unlikely to fail, module should have already been imported - try: - # mod = __import__(module_name) - mod = addon_utils.addons_fake_modules.get(module_name) - except: - import traceback - traceback.print_exc() - return {'CANCELLED'} + mod = addon_utils.addons_fake_modules.get(module_name) + if mod is not None: + info = addon_utils.module_bl_info(mod) + info["show_expanded"] = not info["show_expanded"] - info = addon_utils.module_bl_info(mod) - info["show_expanded"] = not info["show_expanded"] return {'FINISHED'} |