Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release')
-rw-r--r--release/datafiles/splash.pngbin268962 -> 201866 bytes
-rw-r--r--release/environment-mswindows36
-rw-r--r--release/scripts/modules/addon_utils.py19
-rw-r--r--release/scripts/modules/bpy/__init__.py28
-rw-r--r--release/scripts/modules/bpy/ops.py20
-rw-r--r--release/scripts/modules/bpy/path.py15
-rw-r--r--release/scripts/modules/bpy/utils.py14
-rw-r--r--release/scripts/modules/bpy_extras/__init__.py (renamed from release/scripts/modules/image_utils.py)16
-rw-r--r--release/scripts/modules/bpy_extras/image_utils.py110
-rw-r--r--release/scripts/modules/bpy_extras/io_utils.py385
-rw-r--r--release/scripts/modules/bpy_extras/mesh_utils.py510
-rw-r--r--release/scripts/modules/bpy_extras/object_utils.py (renamed from release/scripts/modules/add_object_utils.py)30
-rw-r--r--release/scripts/modules/bpy_extras/view3d_utils.py128
-rw-r--r--release/scripts/modules/bpy_types.py177
-rw-r--r--release/scripts/modules/bpyml_ui.py1
-rw-r--r--release/scripts/modules/console/complete_namespace.py7
-rw-r--r--release/scripts/modules/console/intellisense.py23
-rw-r--r--release/scripts/modules/console_python.py2
-rw-r--r--release/scripts/modules/io_utils.py224
-rw-r--r--release/scripts/modules/mesh_utils.py69
-rw-r--r--release/scripts/modules/rna_prop_ui.py18
-rw-r--r--release/scripts/startup/bl_operators/add_mesh_torus.py21
-rw-r--r--release/scripts/startup/bl_operators/image.py37
-rw-r--r--release/scripts/startup/bl_operators/mesh.py33
-rw-r--r--release/scripts/startup/bl_operators/nla.py123
-rw-r--r--release/scripts/startup/bl_operators/object.py232
-rw-r--r--release/scripts/startup/bl_operators/object_align.py218
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py313
-rw-r--r--release/scripts/startup/bl_operators/object_randomize_transform.py89
-rw-r--r--release/scripts/startup/bl_operators/presets.py55
-rw-r--r--release/scripts/startup/bl_operators/screen_play_rendered_anim.py96
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_follow_active.py5
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_lightmap.py13
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_smart_project.py60
-rw-r--r--release/scripts/startup/bl_operators/wm.py106
-rw-r--r--release/scripts/startup/bl_ui/__init__.py35
-rw-r--r--release/scripts/startup/bl_ui/properties_animviz.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_armature.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_curve.py21
-rw-r--r--release/scripts/startup/bl_ui/properties_data_empty.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py21
-rw-r--r--release/scripts/startup/bl_ui/properties_data_metaball.py17
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py13
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_object_constraint.py21
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py11
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py386
-rw-r--r--release/scripts/startup/bl_ui/properties_texture.py12
-rw-r--r--release/scripts/startup/bl_ui/properties_world.py6
-rw-r--r--release/scripts/startup/bl_ui/space_image.py11
-rw-r--r--release/scripts/startup/bl_ui/space_info.py6
-rw-r--r--release/scripts/startup/bl_ui/space_node.py1
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py33
-rw-r--r--release/scripts/startup/bl_ui/space_text.py61
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py121
-rw-r--r--release/scripts/startup/bl_ui/space_userpref_keymap.py24
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py119
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py31
-rw-r--r--release/scripts/startup/keyingsets_builtins.py2
-rw-r--r--release/scripts/templates/batch_export.py33
-rw-r--r--release/scripts/templates/operator_export.py2
-rw-r--r--release/scripts/templates/operator_mesh_add.py4
-rw-r--r--release/scripts/templates/ui_menu.py49
-rw-r--r--release/scripts/templates/ui_menu_simple.py26
-rw-r--r--release/scripts/templates/ui_panel_simple.py (renamed from release/scripts/templates/panel_simple.py)7
-rw-r--r--release/text/GPL-license.txt2
-rw-r--r--release/text/readme.html10
-rw-r--r--release/windows/installer/00.sconsblender.nsi496
71 files changed, 3330 insertions, 1506 deletions
diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png
index cad2bc5f06c..d6ccdb5b733 100644
--- a/release/datafiles/splash.png
+++ b/release/datafiles/splash.png
Binary files differ
diff --git a/release/environment-mswindows b/release/environment-mswindows
index f8890f89af8..41308533e0b 100644
--- a/release/environment-mswindows
+++ b/release/environment-mswindows
@@ -1,18 +1,18 @@
-# This is a Blender Environment Variable config file.
-#
-# Comment lines start with "#", other lines will be split at the "="
-# and the part before will be used as env var name and the part after
-# as env var value. The value can make reference to previous or
-# prelaunch variables with "%%" and the content will be replaced.
-# Once set, values of variables will not be overwritten.
-#
-# BLENDER_SHARE should be COMMON_APPDATA\\Blender Foundation\\Blender for typical installs.
-# BLENDER_VERSION will be set by the program before processing this file.
-BLENDER_USER_BASE=%USERPROFILE%\\Blender Foundation\\Blender\\%BLENDER_VERSION%
-BLENDER_SYSTEM_BASE=%BLENDER_SHARE%\\%BLENDER_VERSION%
-BLENDER_USER_DATAFILES=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\datafiles
-BLENDER_SYSTEM_DATAFILES=%BLENDER_SHARE%\\%BLENDER_VERSION%\\datafiles
-BLENDER_USER_PY=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\py
-BLENDER_SYSTEM_PY=%BLENDER_SHARE%\\%BLENDER_VERSION%\\py
-BLENDER_USER_PLUGINS=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\plugins
-BLENDER_SYSTEM_PLUGINS=%BLENDER_SHARE%\\%BLENDER_VERSION%\\plugins
+# This is a Blender Environment Variable config file.
+#
+# Comment lines start with "#", other lines will be split at the "="
+# and the part before will be used as env var name and the part after
+# as env var value. The value can make reference to previous or
+# prelaunch variables with "%%" and the content will be replaced.
+# Once set, values of variables will not be overwritten.
+#
+# BLENDER_SHARE should be COMMON_APPDATA\\Blender Foundation\\Blender for typical installs.
+# BLENDER_VERSION will be set by the program before processing this file.
+BLENDER_USER_BASE=%USERPROFILE%\\Blender Foundation\\Blender\\%BLENDER_VERSION%
+BLENDER_SYSTEM_BASE=%BLENDER_SHARE%\\%BLENDER_VERSION%
+BLENDER_USER_DATAFILES=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\datafiles
+BLENDER_SYSTEM_DATAFILES=%BLENDER_SHARE%\\%BLENDER_VERSION%\\datafiles
+BLENDER_USER_PY=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\py
+BLENDER_SYSTEM_PY=%BLENDER_SHARE%\\%BLENDER_VERSION%\\py
+BLENDER_USER_PLUGINS=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\plugins
+BLENDER_SYSTEM_PLUGINS=%BLENDER_SHARE%\\%BLENDER_VERSION%\\plugins
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 3877f711b7f..5aed0581ea9 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -31,6 +31,9 @@ __all__ = (
import bpy as _bpy
+error_duplicates = False
+
+
def paths():
# RELEASE SCRIPTS: official scripts distributed in Blender releases
paths = _bpy.utils.script_paths("addons")
@@ -47,9 +50,10 @@ def paths():
def modules(module_cache):
+ global error_duplicates
import os
- import sys
- import time
+
+ error_duplicates = False
path_list = paths()
@@ -119,7 +123,12 @@ def modules(module_cache):
modules_stale -= {mod_name}
mod = module_cache.get(mod_name)
if mod:
- if mod.__time__ != os.path.getmtime(mod_path):
+ if mod.__file__ != mod_path:
+ print("multiple addons with the same name:\n %r\n %r" %
+ (mod.__file__, mod_path))
+ error_duplicates = True
+
+ elif mod.__time__ != os.path.getmtime(mod_path):
print("reloading addon:", mod_name, mod.__time__, os.path.getmtime(mod_path), mod_path)
del module_cache[mod_name]
mod = None
@@ -173,11 +182,9 @@ def enable(module_name, default_set=True):
:return: the loaded module or None on failier.
:rtype: module
"""
- # note, this still gets added to _bpy_types.TypeMap
import os
import sys
- import bpy_types as _bpy_types
import imp
def handle_error():
@@ -246,8 +253,6 @@ def disable(module_name, default_set=True):
:type module_name: string
"""
import sys
- import bpy_types as _bpy_types
-
mod = sys.modules.get(module_name)
# possible this addon is from a previous session and didnt load a module this time.
diff --git a/release/scripts/modules/bpy/__init__.py b/release/scripts/modules/bpy/__init__.py
index 1df8e9e5588..0add2b3e6cd 100644
--- a/release/scripts/modules/bpy/__init__.py
+++ b/release/scripts/modules/bpy/__init__.py
@@ -22,24 +22,30 @@
Give access to blender data and utility functions.
"""
-# internal blender C module
-import _bpy
-from _bpy import types, props, app
+__all__ = (
+ "app",
+ "context",
+ "data",
+ "ops",
+ "path",
+ "props",
+ "types",
+ "utils",
+)
+
-data = _bpy.data
-context = _bpy.context
+# internal blender C module
+from _bpy import types, props, app, data, context
# python modules
-from . import utils, path
-from . import ops as _ops_module
+from . import utils, path, ops
# fake operator module
-ops = _ops_module.ops_fake_module
-
-import sys as _sys
+ops = ops.ops_fake_module
def _main():
+ import sys as _sys
# Possibly temp. addons path
from os.path import join, dirname, normpath
@@ -59,3 +65,5 @@ def _main():
_main()
+
+del _main
diff --git a/release/scripts/modules/bpy/ops.py b/release/scripts/modules/bpy/ops.py
index f54b0a1fefc..64c5a1a5f5f 100644
--- a/release/scripts/modules/bpy/ops.py
+++ b/release/scripts/modules/bpy/ops.py
@@ -29,7 +29,7 @@ op_as_string = ops_module.as_string
op_get_rna = ops_module.get_rna
-class bpy_ops(object):
+class BPyOps(object):
'''
Fake module like class.
@@ -42,7 +42,7 @@ class bpy_ops(object):
'''
if module.startswith('__'):
raise AttributeError(module)
- return bpy_ops_submodule(module)
+ return BPyOpsSubMod(module)
def __dir__(self):
@@ -67,7 +67,7 @@ class bpy_ops(object):
return "<module like class 'bpy.ops'>"
-class bpy_ops_submodule(object):
+class BPyOpsSubMod(object):
'''
Utility class to fake submodules.
@@ -84,7 +84,7 @@ class bpy_ops_submodule(object):
'''
if func.startswith('__'):
raise AttributeError(func)
- return bpy_ops_submodule_op(self.module, func)
+ return BPyOpsSubModOp(self.module, func)
def __dir__(self):
@@ -103,7 +103,7 @@ class bpy_ops_submodule(object):
return "<module like class 'bpy.ops.%s'>" % self.module
-class bpy_ops_submodule_op(object):
+class BPyOpsSubModOp(object):
'''
Utility class to fake submodule operators.
@@ -151,7 +151,7 @@ class bpy_ops_submodule_op(object):
self.func = func
def poll(self, *args):
- C_dict, C_exec = __class__._parse_args(args)
+ C_dict, C_exec = BPyOpsSubModOp._parse_args(args)
return op_poll(self.idname_py(), C_dict, C_exec)
def idname(self):
@@ -170,16 +170,16 @@ class bpy_ops_submodule_op(object):
wm = context.window_manager
# run to account for any rna values the user changes.
- __class__._scene_update(context)
+ BPyOpsSubModOp._scene_update(context)
if args:
- C_dict, C_exec = __class__._parse_args(args)
+ C_dict, C_exec = BPyOpsSubModOp._parse_args(args)
ret = op_call(self.idname_py(), C_dict, kw, C_exec)
else:
ret = op_call(self.idname_py(), None, kw)
if 'FINISHED' in ret and context.window_manager == wm:
- __class__._scene_update(context)
+ BPyOpsSubModOp._scene_update(context)
return ret
@@ -208,4 +208,4 @@ class bpy_ops_submodule_op(object):
return "<function bpy.ops.%s.%s at 0x%x'>" % \
(self.module, self.func, id(self))
-ops_fake_module = bpy_ops()
+ops_fake_module = BPyOps()
diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py
index f7e5b988cc8..eb1a5ffc455 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -35,7 +35,7 @@ def abspath(path, start=None):
:type start: string
"""
if path.startswith("//"):
- return _os.path.join(_os.path.dirname(_bpy.data.filepath if start is None else start), path[2:])
+ return _os.path.join(_os.path.dirname(_bpy.data.filepath) if start is None else start, path[2:])
return path
@@ -117,7 +117,7 @@ def display_name_from_filepath(name):
"""
Returns the path stripped of directort and extension, ensured to be utf8 compatible.
"""
- return _os.path.splitext(_os.path.basename(name))[0].encode("utf8", "replace").decode("utf8")
+ return _os.path.splitext(basename(name))[0].encode("utf8", "replace").decode("utf8")
def resolve_ncase(path):
@@ -135,7 +135,7 @@ def resolve_ncase(path):
filename = os.path.basename(path) # filename may be a directory or a file
dirpath = os.path.dirname(path)
- suffix = ""
+ suffix = path[:0] # "" but ensure byte/str match
if not filename: # dir ends with a slash?
if len(dirpath) < len(path):
suffix = path[:len(path) - len(dirpath)]
@@ -231,3 +231,12 @@ def module_names(path, recursive=False):
modules.append(("%s.%s" % (filename, mod_name), mod_path))
return modules
+
+
+def basename(path):
+ """
+ Equivalent to os.path.basename, but skips a "//" suffix.
+
+ Use for Windows compatibility.
+ """
+ return _os.path.basename(path[2:] if path[:2] in {"//", b"//"} else path)
diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py
index 43c1692b192..6e3cbb244e3 100644
--- a/release/scripts/modules/bpy/utils.py
+++ b/release/scripts/modules/bpy/utils.py
@@ -35,6 +35,7 @@ import addon_utils as _addon_utils
_script_module_dirs = "startup", "modules", "bge_components"
+
def _test_import(module_name, loaded_modules):
use_time = _bpy.app.debug
@@ -297,11 +298,18 @@ _presets = _os.path.join(_scripts[0], "presets") # FIXME - multiple paths
def preset_paths(subdir):
"""
Returns a list of paths for a specific preset.
+
+ :arg subdir: preset subdirectory (must not be an absolute path).
+ :type subdir: string
+ :return: script paths.
+ :rtype: list
"""
dirs = []
for path in script_paths("presets", all=True):
directory = _os.path.join(path, subdir)
- if _os.path.isdir(directory):
+ if not directory.startswith(path):
+ raise Exception("invalid subdir given %r" % subdir)
+ elif _os.path.isdir(directory):
dirs.append(directory)
return dirs
@@ -378,7 +386,9 @@ def preset_find(name, preset_path, display_name=False):
def keyconfig_set(filepath):
from os.path import basename, splitext
- print("loading preset:", filepath)
+ if _bpy.app.debug:
+ print("loading preset:", filepath)
+
keyconfigs = _bpy.context.window_manager.keyconfigs
keyconfigs_old = keyconfigs[:]
diff --git a/release/scripts/modules/image_utils.py b/release/scripts/modules/bpy_extras/__init__.py
index 39e49ee1f96..06d41fa670e 100644
--- a/release/scripts/modules/image_utils.py
+++ b/release/scripts/modules/bpy_extras/__init__.py
@@ -18,10 +18,14 @@
# <pep8 compliant>
+"""
+Utility modules assosiated with the bpy module.
+"""
-def image_load(filepath, dirpath, place_holder=False, recursive=False, convert_callback=None):
- import bpy
- try:
- return bpy.data.images.load(filepath)
- except RuntimeError:
- return bpy.data.images.new("Untitled", 128, 128)
+__all__ = (
+ "object_utils",
+ "io_utils",
+ "image_utils",
+ "mesh_utils",
+ "view3d_utils",
+)
diff --git a/release/scripts/modules/bpy_extras/image_utils.py b/release/scripts/modules/bpy_extras/image_utils.py
new file mode 100644
index 00000000000..e56c1c651c4
--- /dev/null
+++ b/release/scripts/modules/bpy_extras/image_utils.py
@@ -0,0 +1,110 @@
+# ##### 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>
+
+__all__ = (
+ "load_image",
+)
+
+
+# limited replacement for BPyImage.comprehensiveImageLoad
+def load_image(imagepath,
+ dirname="",
+ place_holder=False,
+ recursive=False,
+ ncase_cmp=True,
+ convert_callback=None,
+ verbose=False,
+ ):
+ """
+ Return an image from the file path with options to search multiple paths and
+ return a placeholder if its not found.
+
+ :arg filepath: The image filename
+ If a path precedes it, this will be searched as well.
+ :type filepath: string
+ :arg dirname: is the directory where the image may be located - any file at
+ the end will be ignored.
+ :type dirname: string
+ :arg place_holder: if True a new place holder image will be created.
+ this is usefull so later you can relink the image to its original data.
+ :type place_holder: bool
+ :arg recursive: If True, directories will be recursivly searched.
+ Be carefull with this if you have files in your root directory because
+ it may take a long time.
+ :type recursive: bool
+ :arg ncase_cmp: on non windows systems, find the correct case for the file.
+ :type ncase_cmp: bool
+ :arg convert_callback: a function that takes an existing path and returns a new one.
+ Use this when loading image formats blender may not support, the CONVERT_CALLBACK
+ can take the path for a GIF (for example), convert it to a PNG and return the PNG's path.
+ For formats blender can read, simply return the path that is given.
+ :type convert_callback: function
+ :return: an image or None
+ :rtype: :class:`Image`
+ """
+ import os
+ import bpy
+
+ # TODO: recursive
+
+ def _image_load(path):
+ import bpy
+
+ if convert_callback:
+ path = convert_callback(path)
+
+ image = bpy.data.images.load(path)
+
+ if verbose:
+ print(" image loaded '%s'" % path)
+
+ return image
+
+ if verbose:
+ print("load_image('%s', '%s', ...)" % (imagepath, dirname))
+
+ if os.path.exists(imagepath):
+ return _image_load(imagepath)
+
+ variants = [imagepath]
+
+ if dirname:
+ variants += [os.path.join(dirname, imagepath),
+ os.path.join(dirname, bpy.path.basename(imagepath)),
+ ]
+
+ for filepath_test in variants:
+ if ncase_cmp:
+ ncase_variants = filepath_test, bpy.path.resolve_ncase(filepath_test)
+ else:
+ ncase_variants = (filepath_test, )
+
+ for nfilepath in ncase_variants:
+ if os.path.exists(nfilepath):
+ return _image_load(nfilepath)
+
+ if place_holder:
+ image = bpy.data.images.new(bpy.path.basename(imagepath), 128, 128)
+ # allow the path to be resolved later
+ image.filepath = imagepath
+ return image
+
+ # TODO comprehensiveImageLoad also searched in bpy.config.textureDir
+ return None
diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py
new file mode 100644
index 00000000000..0a3f1392653
--- /dev/null
+++ b/release/scripts/modules/bpy_extras/io_utils.py
@@ -0,0 +1,385 @@
+# ##### 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>
+
+__all__ = (
+ "ExportHelper",
+ "ImportHelper",
+ "axis_conversion",
+ "axis_conversion_ensure",
+ "create_derived_objects",
+ "free_derived_objects",
+ "unpack_list",
+ "unpack_face_list",
+ "path_reference",
+ "path_reference_copy",
+ "path_reference_mode",
+ "unique_name"
+)
+
+import bpy
+from bpy.props import StringProperty, BoolProperty, EnumProperty
+
+
+class ExportHelper:
+ filepath = StringProperty(name="File Path", description="Filepath used for exporting the file", maxlen=1024, default="", subtype='FILE_PATH')
+ check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
+
+ # subclasses can override with decorator
+ # True == use ext, False == no ext, None == do nothing.
+ check_extension = True
+
+ def invoke(self, context, event):
+ import os
+ if not self.filepath:
+ blend_filepath = context.blend_data.filepath
+ if not blend_filepath:
+ blend_filepath = "untitled"
+ else:
+ blend_filepath = os.path.splitext(blend_filepath)[0]
+
+ self.filepath = blend_filepath + self.filename_ext
+
+ context.window_manager.fileselect_add(self)
+ return {'RUNNING_MODAL'}
+
+ def check(self, context):
+ check_extension = self.check_extension
+
+ if check_extension is None:
+ return False
+
+ filepath = bpy.path.ensure_ext(self.filepath, self.filename_ext if check_extension else "")
+
+ if filepath != self.filepath:
+ self.filepath = filepath
+ return True
+
+ return False
+
+
+class ImportHelper:
+ filepath = StringProperty(name="File Path", description="Filepath used for importing the file", maxlen=1024, default="", subtype='FILE_PATH')
+
+ def invoke(self, context, event):
+ context.window_manager.fileselect_add(self)
+ return {'RUNNING_MODAL'}
+
+
+# Axis conversion function, not pretty LUT
+# use lookup tabes to convert between any axis
+_axis_convert_matrix = (
+ ((-1.0, 0.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, 1.0)),
+ ((-1.0, 0.0, 0.0), (0.0, 0.0, -1.0), (0.0, -1.0, 0.0)),
+ ((-1.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, 1.0, 0.0)),
+ ((-1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, -1.0)),
+ ((0.0, -1.0, 0.0), (-1.0, 0.0, 0.0), (0.0, 0.0, -1.0)),
+ ((0.0, -1.0, 0.0), (0.0, 0.0, -1.0), (1.0, 0.0, 0.0)),
+ ((0.0, -1.0, 0.0), (0.0, 0.0, 1.0), (-1.0, 0.0, 0.0)),
+ ((0.0, -1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0)),
+ ((0.0, 0.0, -1.0), (-1.0, 0.0, 0.0), (0.0, 1.0, 0.0)),
+ ((0.0, 0.0, -1.0), (0.0, -1.0, 0.0), (-1.0, 0.0, 0.0)),
+ ((0.0, 0.0, -1.0), (0.0, 1.0, 0.0), (1.0, 0.0, 0.0)),
+ ((0.0, 0.0, -1.0), (1.0, 0.0, 0.0), (0.0, -1.0, 0.0)),
+ ((0.0, 0.0, 1.0), (-1.0, 0.0, 0.0), (0.0, -1.0, 0.0)),
+ ((0.0, 0.0, 1.0), (0.0, -1.0, 0.0), (1.0, 0.0, 0.0)),
+ ((0.0, 0.0, 1.0), (0.0, 1.0, 0.0), (-1.0, 0.0, 0.0)),
+ ((0.0, 0.0, 1.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)),
+ ((0.0, 1.0, 0.0), (-1.0, 0.0, 0.0), (0.0, 0.0, 1.0)),
+ ((0.0, 1.0, 0.0), (0.0, 0.0, -1.0), (-1.0, 0.0, 0.0)),
+ ((0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (1.0, 0.0, 0.0)),
+ ((0.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, -1.0)),
+ ((1.0, 0.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, -1.0)),
+ ((1.0, 0.0, 0.0), (0.0, 0.0, -1.0), (0.0, 1.0, 0.0)),
+ ((1.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, -1.0, 0.0)),
+ )
+
+# store args as a single int
+# (X Y Z -X -Y -Z) --> (0, 1, 2, 3, 4, 5)
+# each value is ((src_forward, src_up), (dst_forward, dst_up))
+# where all 4 values are or'd into a single value...
+# (i1<<0 | i1<<3 | i1<<6 | i1<<9)
+_axis_convert_lut = (
+ {0x8C8, 0x4D0, 0x2E0, 0xAE8, 0x701, 0x511, 0x119, 0xB29, 0x682, 0x88A, 0x09A, 0x2A2, 0x80B, 0x413, 0x223, 0xA2B, 0x644, 0x454, 0x05C, 0xA6C, 0x745, 0x94D, 0x15D, 0x365},
+ {0xAC8, 0x8D0, 0x4E0, 0x2E8, 0x741, 0x951, 0x159, 0x369, 0x702, 0xB0A, 0x11A, 0x522, 0xA0B, 0x813, 0x423, 0x22B, 0x684, 0x894, 0x09C, 0x2AC, 0x645, 0xA4D, 0x05D, 0x465},
+ {0x4C8, 0x2D0, 0xAE0, 0x8E8, 0x681, 0x291, 0x099, 0x8A9, 0x642, 0x44A, 0x05A, 0xA62, 0x40B, 0x213, 0xA23, 0x82B, 0x744, 0x354, 0x15C, 0x96C, 0x705, 0x50D, 0x11D, 0xB25},
+ {0x2C8, 0xAD0, 0x8E0, 0x4E8, 0x641, 0xA51, 0x059, 0x469, 0x742, 0x34A, 0x15A, 0x962, 0x20B, 0xA13, 0x823, 0x42B, 0x704, 0xB14, 0x11C, 0x52C, 0x685, 0x28D, 0x09D, 0x8A5},
+ {0x708, 0xB10, 0x120, 0x528, 0x8C1, 0xAD1, 0x2D9, 0x4E9, 0x942, 0x74A, 0x35A, 0x162, 0x64B, 0xA53, 0x063, 0x46B, 0x804, 0xA14, 0x21C, 0x42C, 0x885, 0x68D, 0x29D, 0x0A5},
+ {0xB08, 0x110, 0x520, 0x728, 0x941, 0x151, 0x359, 0x769, 0x802, 0xA0A, 0x21A, 0x422, 0xA4B, 0x053, 0x463, 0x66B, 0x884, 0x094, 0x29C, 0x6AC, 0x8C5, 0xACD, 0x2DD, 0x4E5},
+ {0x508, 0x710, 0xB20, 0x128, 0x881, 0x691, 0x299, 0x0A9, 0x8C2, 0x4CA, 0x2DA, 0xAE2, 0x44B, 0x653, 0xA63, 0x06B, 0x944, 0x754, 0x35C, 0x16C, 0x805, 0x40D, 0x21D, 0xA25},
+ {0x108, 0x510, 0x720, 0xB28, 0x801, 0x411, 0x219, 0xA29, 0x882, 0x08A, 0x29A, 0x6A2, 0x04B, 0x453, 0x663, 0xA6B, 0x8C4, 0x4D4, 0x2DC, 0xAEC, 0x945, 0x14D, 0x35D, 0x765},
+ {0x748, 0x350, 0x160, 0x968, 0xAC1, 0x2D1, 0x4D9, 0x8E9, 0xA42, 0x64A, 0x45A, 0x062, 0x68B, 0x293, 0x0A3, 0x8AB, 0xA04, 0x214, 0x41C, 0x82C, 0xB05, 0x70D, 0x51D, 0x125},
+ {0x948, 0x750, 0x360, 0x168, 0xB01, 0x711, 0x519, 0x129, 0xAC2, 0x8CA, 0x4DA, 0x2E2, 0x88B, 0x693, 0x2A3, 0x0AB, 0xA44, 0x654, 0x45C, 0x06C, 0xA05, 0x80D, 0x41D, 0x225},
+ {0x348, 0x150, 0x960, 0x768, 0xA41, 0x051, 0x459, 0x669, 0xA02, 0x20A, 0x41A, 0x822, 0x28B, 0x093, 0x8A3, 0x6AB, 0xB04, 0x114, 0x51C, 0x72C, 0xAC5, 0x2CD, 0x4DD, 0x8E5},
+ {0x148, 0x950, 0x760, 0x368, 0xA01, 0x811, 0x419, 0x229, 0xB02, 0x10A, 0x51A, 0x722, 0x08B, 0x893, 0x6A3, 0x2AB, 0xAC4, 0x8D4, 0x4DC, 0x2EC, 0xA45, 0x04D, 0x45D, 0x665},
+ {0x688, 0x890, 0x0A0, 0x2A8, 0x4C1, 0x8D1, 0xAD9, 0x2E9, 0x502, 0x70A, 0xB1A, 0x122, 0x74B, 0x953, 0x163, 0x36B, 0x404, 0x814, 0xA1C, 0x22C, 0x445, 0x64D, 0xA5D, 0x065},
+ {0x888, 0x090, 0x2A0, 0x6A8, 0x501, 0x111, 0xB19, 0x729, 0x402, 0x80A, 0xA1A, 0x222, 0x94B, 0x153, 0x363, 0x76B, 0x444, 0x054, 0xA5C, 0x66C, 0x4C5, 0x8CD, 0xADD, 0x2E5},
+ {0x288, 0x690, 0x8A0, 0x0A8, 0x441, 0x651, 0xA59, 0x069, 0x4C2, 0x2CA, 0xADA, 0x8E2, 0x34B, 0x753, 0x963, 0x16B, 0x504, 0x714, 0xB1C, 0x12C, 0x405, 0x20D, 0xA1D, 0x825},
+ {0x088, 0x290, 0x6A0, 0x8A8, 0x401, 0x211, 0xA19, 0x829, 0x442, 0x04A, 0xA5A, 0x662, 0x14B, 0x353, 0x763, 0x96B, 0x4C4, 0x2D4, 0xADC, 0x8EC, 0x505, 0x10D, 0xB1D, 0x725},
+ {0x648, 0x450, 0x060, 0xA68, 0x2C1, 0x4D1, 0x8D9, 0xAE9, 0x282, 0x68A, 0x89A, 0x0A2, 0x70B, 0x513, 0x123, 0xB2B, 0x204, 0x414, 0x81C, 0xA2C, 0x345, 0x74D, 0x95D, 0x165},
+ {0xA48, 0x650, 0x460, 0x068, 0x341, 0x751, 0x959, 0x169, 0x2C2, 0xACA, 0x8DA, 0x4E2, 0xB0B, 0x713, 0x523, 0x12B, 0x284, 0x694, 0x89C, 0x0AC, 0x205, 0xA0D, 0x81D, 0x425},
+ {0x448, 0x050, 0xA60, 0x668, 0x281, 0x091, 0x899, 0x6A9, 0x202, 0x40A, 0x81A, 0xA22, 0x50B, 0x113, 0xB23, 0x72B, 0x344, 0x154, 0x95C, 0x76C, 0x2C5, 0x4CD, 0x8DD, 0xAE5},
+ {0x048, 0xA50, 0x660, 0x468, 0x201, 0xA11, 0x819, 0x429, 0x342, 0x14A, 0x95A, 0x762, 0x10B, 0xB13, 0x723, 0x52B, 0x2C4, 0xAD4, 0x8DC, 0x4EC, 0x285, 0x08D, 0x89D, 0x6A5},
+ {0x808, 0xA10, 0x220, 0x428, 0x101, 0xB11, 0x719, 0x529, 0x142, 0x94A, 0x75A, 0x362, 0x8CB, 0xAD3, 0x2E3, 0x4EB, 0x044, 0xA54, 0x65C, 0x46C, 0x085, 0x88D, 0x69D, 0x2A5},
+ {0xA08, 0x210, 0x420, 0x828, 0x141, 0x351, 0x759, 0x969, 0x042, 0xA4A, 0x65A, 0x462, 0xACB, 0x2D3, 0x4E3, 0x8EB, 0x084, 0x294, 0x69C, 0x8AC, 0x105, 0xB0D, 0x71D, 0x525},
+ {0x408, 0x810, 0xA20, 0x228, 0x081, 0x891, 0x699, 0x2A9, 0x102, 0x50A, 0x71A, 0xB22, 0x4CB, 0x8D3, 0xAE3, 0x2EB, 0x144, 0x954, 0x75C, 0x36C, 0x045, 0x44D, 0x65D, 0xA65},
+ )
+
+_axis_convert_num = {'X': 0, 'Y': 1, 'Z': 2, '-X': 3, '-Y': 4, '-Z': 5}
+
+
+def axis_conversion(from_forward='Y', from_up='Z', to_forward='Y', to_up='Z'):
+ """
+ Each argument us an axis in ['X', 'Y', 'Z', '-X', '-Y', '-Z']
+ where the first 2 are a source and the second 2 are the target.
+ """
+ from mathutils import Matrix
+ from functools import reduce
+
+ if from_forward == to_forward and from_up == to_up:
+ return Matrix().to_3x3()
+
+ if from_forward[-1] == from_up[-1] or to_forward[-1] == to_up[-1]:
+ raise Exception("invalid axis arguments passed, "
+ "can't use up/forward on the same axis.")
+
+ value = reduce(int.__or__, (_axis_convert_num[a] << (i * 3)
+ for i, a in enumerate((from_forward,
+ from_up,
+ to_forward,
+ to_up,
+ ))))
+
+ for i, axis_lut in enumerate(_axis_convert_lut):
+ if value in axis_lut:
+ return Matrix(_axis_convert_matrix[i])
+ assert(0)
+
+
+def axis_conversion_ensure(operator, forward_attr, up_attr):
+ """
+ Function to ensure an operator has valid axis conversion settings, intended
+ to be used from :class:`Operator.check`.
+
+ :arg operator: the operator to access axis attributes from.
+ :type operator: :class:`Operator`
+ :arg forward_attr: attribute storing the forward axis
+ :type forward_attr: string
+ :arg up_attr: attribute storing the up axis
+ :type up_attr: string
+ :return: True if the value was modified.
+ :rtype: boolean
+ """
+ def validate(axis_forward, axis_up):
+ if axis_forward[-1] == axis_up[-1]:
+ axis_up = axis_up[0:-1] + 'XYZ'[('XYZ'.index(axis_up[-1]) + 1) % 3]
+
+ return axis_forward, axis_up
+
+ change = False
+
+ axis = getattr(operator, forward_attr), getattr(operator, up_attr)
+ axis_new = validate(*axis)
+
+ if axis != axis_new:
+ setattr(operator, forward_attr, axis_new[0])
+ setattr(operator, up_attr, axis_new[1])
+
+ return True
+ else:
+ return False
+
+
+# return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects()
+def create_derived_objects(scene, ob):
+ if ob.parent and ob.parent.dupli_type in {'VERTS', 'FACES'}:
+ return False, None
+
+ if ob.dupli_type != 'NONE':
+ ob.dupli_list_create(scene)
+ return True, [(dob.object, dob.matrix) for dob in ob.dupli_list]
+ else:
+ return False, [(ob, ob.matrix_world)]
+
+
+def free_derived_objects(ob):
+ ob.dupli_list_clear()
+
+
+def unpack_list(list_of_tuples):
+ flat_list = []
+ flat_list_extend = flat_list.extend # a tich faster
+ for t in list_of_tuples:
+ flat_list_extend(t)
+ return flat_list
+
+
+# same as above except that it adds 0 for triangle faces
+def unpack_face_list(list_of_tuples):
+ #allocate the entire list
+ flat_ls = [0] * (len(list_of_tuples) * 4)
+ i = 0
+
+ for t in list_of_tuples:
+ if len(t) == 3:
+ if t[2] == 0:
+ t = t[1], t[2], t[0]
+ else: # assuem quad
+ if t[3] == 0 or t[2] == 0:
+ t = t[2], t[3], t[0], t[1]
+
+ flat_ls[i:i + len(t)] = t
+ i += 4
+ return flat_ls
+
+
+path_reference_mode = EnumProperty(
+ name="Path Mode",
+ description="Method used to reference paths",
+ items=(('AUTO', "Auto", "Use Relative paths with subdirectories only"),
+ ('ABSOLUTE', "Absolute", "Always write absolute paths"),
+ ('RELATIVE', "Relative", "Always write relative patsh (where possible)"),
+ ('MATCH', "Match", "Match Absolute/Relative setting with input path"),
+ ('STRIP', "Strip Path", "Filename only"),
+ ('COPY', "Copy", "copy the file to the destination path (or subdirectory)"),
+ ),
+ default='AUTO'
+ )
+
+
+def path_reference(filepath, base_src, base_dst, mode='AUTO', copy_subdir="", copy_set=None):
+ """
+ Return a filepath relative to a destination directory, for use with
+ exporters.
+
+ :arg filepath: the file path to return, supporting blenders relative '//' prefix.
+ :type filepath: string
+ :arg base_src: the directory the *filepath* is relative too (normally the blend file).
+ :type base_src: string
+ :arg base_dst: the directory the *filepath* will be referenced from (normally the export path).
+ :type base_dst: string
+ :arg mode: the method used get the path in ['AUTO', 'ABSOLUTE', 'RELATIVE', 'MATCH', 'STRIP', 'COPY']
+ :type mode: string
+ :arg copy_subdir: the subdirectory of *base_dst* to use when mode='COPY'.
+ :type copy_subdir: string
+ :arg copy_set: collect from/to pairs when mode='COPY', pass to *path_reference_copy* when exportign is done.
+ :type copy_set: set
+ :return: the new filepath.
+ :rtype: string
+ """
+ import os
+ is_relative = filepath.startswith("//")
+ filepath_abs = os.path.normpath(bpy.path.abspath(filepath, base_src))
+
+ if mode in ('ABSOLUTE', 'RELATIVE', 'STRIP'):
+ pass
+ elif mode == 'MATCH':
+ mode = 'RELATIVE' if is_relative else 'ABSOLUTE'
+ elif mode == 'AUTO':
+ mode = 'RELATIVE' if bpy.path.is_subdir(filepath, base_dst) else 'ABSOLUTE'
+ elif mode == 'COPY':
+ if copy_subdir:
+ subdir_abs = os.path.join(os.path.normpath(base_dst), copy_subdir)
+ else:
+ subdir_abs = os.path.normpath(base_dst)
+
+ filepath_cpy = os.path.join(subdir_abs, os.path.basename(filepath))
+
+ copy_set.add((filepath_abs, filepath_cpy))
+
+ filepath_abs = filepath_cpy
+ mode = 'RELATIVE'
+ else:
+ raise Exception("invalid mode given %r" % mode)
+
+ if mode == 'ABSOLUTE':
+ return filepath_abs
+ elif mode == 'RELATIVE':
+ return os.path.relpath(filepath_abs, base_dst)
+ elif mode == 'STRIP':
+ return os.path.basename(filepath_abs)
+
+
+def path_reference_copy(copy_set, report=print):
+ """
+ Execute copying files of path_reference
+
+ :arg copy_set: set of (from, to) pairs to copy.
+ :type copy_set: set
+ :arg report: function used for reporting warnings, takes a string argument.
+ :type report: function
+ """
+ if not copy_set:
+ return
+
+ import os
+ import shutil
+
+ for file_src, file_dst in copy_set:
+ if not os.path.exists(file_src):
+ report("missing %r, not copying" % file_src)
+ elif os.path.exists(file_dst) and os.path.samefile(file_src, file_dst):
+ pass
+ else:
+ dir_to = os.path.dirname(file_dst)
+
+ if not os.path.isdir(dir_to):
+ os.makedirs(dir_to)
+
+ shutil.copy(file_src, file_dst)
+
+
+def unique_name(key, name, name_dict, name_max=-1, clean_func=None):
+ """
+ Helper function for storing unique names which may have special characters
+ stripped and restricted to a maximum length.
+
+ :arg key: unique item this name belongs to, name_dict[key] will be reused
+ when available.
+ This can be the object, mesh, material, etc instance its self.
+ :type key: any hashable object assosiated with the *name*.
+ :arg name: The name used to create a unique value in *name_dict*.
+ :type name: string
+ :arg name_dict: This is used to cache namespace to ensure no collisions
+ occur, this should be an empty dict initially and only modified by this
+ function.
+ :type name_dict: dict
+ :arg clean_func: Function to call on *name* before creating a unique value.
+ :type clean_func: function
+ """
+ name_new = name_dict.get(key)
+ if name_new is None:
+ count = 1
+ name_dict_values = name_dict.values()
+ name_new = name_new_orig = name if clean_func is None else clean_func(name)
+
+ if name_max == -1:
+ while name_new in name_dict_values:
+ name_new = "%s.%03d" % (name_new_orig, count)
+ count += 1
+ else:
+ name_new = name_new[:name_max]
+ while name_new in name_dict_values:
+ count_str = "%03d" % count
+ name_new = "%.*s.%s" % (name_max - (len(count_str) + 1), name_new_orig, count_str)
+ count += 1
+
+ name_dict[key] = name_new
+
+ return name_new
diff --git a/release/scripts/modules/bpy_extras/mesh_utils.py b/release/scripts/modules/bpy_extras/mesh_utils.py
new file mode 100644
index 00000000000..ecd620ff2c9
--- /dev/null
+++ b/release/scripts/modules/bpy_extras/mesh_utils.py
@@ -0,0 +1,510 @@
+# ##### 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-80 compliant>
+
+__all__ = (
+ "mesh_linked_faces",
+ "edge_face_count_dict",
+ "edge_face_count",
+ "edge_loops_from_faces",
+ "edge_loops_from_edges",
+ "ngon_tesselate",
+ "face_random_points",
+)
+
+
+def mesh_linked_faces(mesh):
+ """
+ Splits the mesh into connected faces, use this for seperating cubes from
+ other mesh elements within 1 mesh datablock.
+
+ :arg mesh: the mesh used to group with.
+ :type mesh: :class:`Mesh`
+ :return: lists of lists containing faces.
+ :rtype: list
+ """
+
+ # Build vert face connectivity
+ vert_faces = [[] for i in range(len(mesh.vertices))]
+ for f in mesh.faces:
+ for v in f.vertices:
+ vert_faces[v].append(f)
+
+ # sort faces into connectivity groups
+ face_groups = [[f] for f in mesh.faces]
+ face_mapping = list(range(len(mesh.faces))) # map old, new face location
+
+ # Now clump faces iterativly
+ ok = True
+ while ok:
+ ok = False
+
+ for i, f in enumerate(mesh.faces):
+ mapped_index = face_mapping[f.index]
+ mapped_group = face_groups[mapped_index]
+
+ for v in f.vertices:
+ for nxt_f in vert_faces[v]:
+ if nxt_f != f:
+ nxt_mapped_index = face_mapping[nxt_f.index]
+
+ # We are not a part of the same group
+ if mapped_index != nxt_mapped_index:
+ ok = True
+
+ # Assign mapping to this group so they
+ # all map to this group
+ for grp_f in face_groups[nxt_mapped_index]:
+ face_mapping[grp_f.index] = mapped_index
+
+ # Move faces into this group
+ mapped_group.extend(face_groups[nxt_mapped_index])
+
+ # remove reference to the list
+ face_groups[nxt_mapped_index] = None
+
+ # return all face groups that are not null
+ # this is all the faces that are connected in their own lists.
+ return [fg for fg in face_groups if fg]
+
+
+def edge_face_count_dict(mesh):
+ """
+ :return: dict of edge keys with their value set to the number of
+ faces using each edge.
+ :rtype: dict
+ """
+ face_edge_keys = [face.edge_keys for face in mesh.faces]
+ face_edge_count = {}
+ for face_keys in face_edge_keys:
+ for key in face_keys:
+ try:
+ face_edge_count[key] += 1
+ except:
+ face_edge_count[key] = 1
+
+ return face_edge_count
+
+
+def edge_face_count(mesh):
+ """
+ :return: list face users for each item in mesh.edges.
+ :rtype: list
+ """
+ edge_face_count = edge_face_count_dict(mesh)
+ get = dict.get
+ return [get(edge_face_count, ed.key, 0) for ed in mesh.edges]
+
+
+def edge_loops_from_faces(mesh, faces=None, seams=()):
+ """
+ Edge loops defined by faces
+
+ Takes me.faces or a list of faces and returns the edge loops
+ These edge loops are the edges that sit between quads, so they dont touch
+ 1 quad, note: not connected will make 2 edge loops,
+ both only containing 2 edges.
+
+ return a list of edge key lists
+ [[(0, 1), (4, 8), (3, 8)], ...]
+
+ :arg mesh: the mesh used to get edge loops from.
+ :type mesh: :class:`Mesh`
+ :arg faces: optional face list to only use some of the meshes faces.
+ :type faces: :class:`MeshFaces`, sequence or or NoneType
+ :return: return a list of edge vertex index lists.
+ :rtype: list
+ """
+
+ OTHER_INDEX = 2, 3, 0, 1 # opposite face index
+
+ if faces is None:
+ faces = mesh.faces
+
+ edges = {}
+
+ for f in faces:
+# if len(f) == 4:
+ if f.vertices_raw[3] != 0:
+ edge_keys = f.edge_keys
+ for i, edkey in enumerate(f.edge_keys):
+ edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]])
+
+ for edkey in seams:
+ edges[edkey] = []
+
+ # Collect edge loops here
+ edge_loops = []
+
+ for edkey, ed_adj in edges.items():
+ if 0 < len(ed_adj) < 3: # 1 or 2
+ # Seek the first edge
+ context_loop = [edkey, ed_adj[0]]
+ edge_loops.append(context_loop)
+ if len(ed_adj) == 2:
+ other_dir = ed_adj[1]
+ else:
+ other_dir = None
+
+ ed_adj[:] = []
+
+ flipped = False
+
+ while 1:
+ # from knowing the last 2, look for th next.
+ ed_adj = edges[context_loop[-1]]
+ if len(ed_adj) != 2:
+ # the original edge had 2 other edges
+ if other_dir and flipped == False:
+ flipped = True # only flip the list once
+ context_loop.reverse()
+ ed_adj[:] = []
+ context_loop.append(other_dir) # save 1 lookiup
+
+ ed_adj = edges[context_loop[-1]]
+ if len(ed_adj) != 2:
+ ed_adj[:] = []
+ break
+ else:
+ ed_adj[:] = []
+ break
+
+ i = ed_adj.index(context_loop[-2])
+ context_loop.append(ed_adj[not i])
+
+ # Dont look at this again
+ ed_adj[:] = []
+
+ return edge_loops
+
+
+def edge_loops_from_edges(mesh, edges=None):
+ """
+ Edge loops defined by edges
+
+ Takes me.edges or a list of edges and returns the edge loops
+
+ return a list of vertex indices.
+ [ [1, 6, 7, 2], ...]
+
+ closed loops have matching start and end values.
+ """
+ line_polys = []
+
+ # Get edges not used by a face
+ if edges is None:
+ edges = mesh.edges
+
+ if not hasattr(edges, "pop"):
+ edges = edges[:]
+
+ while edges:
+ current_edge = edges.pop()
+ vert_end, vert_start = current_edge.vertices[:]
+ line_poly = [vert_start, vert_end]
+
+ ok = True
+ while ok:
+ ok = False
+ #for i, ed in enumerate(edges):
+ i = len(edges)
+ while i:
+ i -= 1
+ ed = edges[i]
+ v1, v2 = ed.vertices
+ if v1 == vert_end:
+ line_poly.append(v2)
+ vert_end = line_poly[-1]
+ ok = 1
+ del edges[i]
+ # break
+ elif v2 == vert_end:
+ line_poly.append(v1)
+ vert_end = line_poly[-1]
+ ok = 1
+ del edges[i]
+ #break
+ elif v1 == vert_start:
+ line_poly.insert(0, v2)
+ vert_start = line_poly[0]
+ ok = 1
+ del edges[i]
+ # break
+ elif v2 == vert_start:
+ line_poly.insert(0, v1)
+ vert_start = line_poly[0]
+ ok = 1
+ del edges[i]
+ #break
+ line_polys.append(line_poly)
+
+ return line_polys
+
+
+def ngon_tesselate(from_data, indices, fix_loops=True):
+ '''
+ Takes a polyline of indices (fgon) and returns a list of face
+ indicie lists. Designed to be used for importers that need indices for an
+ fgon to create from existing verts.
+
+ from_data: either a mesh, or a list/tuple of vectors.
+ indices: a list of indices to use this list is the ordered closed polyline
+ to fill, and can be a subset of the data given.
+ fix_loops: If this is enabled polylines that use loops to make multiple
+ polylines are delt with correctly.
+ '''
+
+ from mathutils.geometry import tesselate_polygon
+ from mathutils import Vector
+ vector_to_tuple = Vector.to_tuple
+
+ if not indices:
+ return []
+
+ def mlen(co):
+ # manhatten length of a vector, faster then length
+ return abs(co[0]) + abs(co[1]) + abs(co[2])
+
+ def vert_treplet(v, i):
+ return v, vector_to_tuple(v, 6), i, mlen(v)
+
+ def ed_key_mlen(v1, v2):
+ if v1[3] > v2[3]:
+ return v2[1], v1[1]
+ else:
+ return v1[1], v2[1]
+
+ if not fix_loops:
+ '''
+ Normal single concave loop filling
+ '''
+ if type(from_data) in (tuple, list):
+ verts = [Vector(from_data[i]) for ii, i in enumerate(indices)]
+ else:
+ verts = [from_data.vertices[i].co for ii, i in enumerate(indices)]
+
+ # same as reversed(range(1, len(verts))):
+ for i in range(len(verts) - 1, 0, -1):
+ if verts[i][1] == verts[i - 1][0]:
+ verts.pop(i - 1)
+
+ fill = tesselate_polygon([verts])
+
+ else:
+ '''
+ Seperate this loop into multiple loops be finding edges that are
+ used twice. This is used by lightwave LWO files a lot
+ '''
+
+ if type(from_data) in (tuple, list):
+ verts = [vert_treplet(Vector(from_data[i]), ii)
+ for ii, i in enumerate(indices)]
+ else:
+ verts = [vert_treplet(from_data.vertices[i].co, ii)
+ for ii, i in enumerate(indices)]
+
+ edges = [(i, i - 1) for i in range(len(verts))]
+ if edges:
+ edges[0] = (0, len(verts) - 1)
+
+ if not verts:
+ return []
+
+ edges_used = set()
+ edges_doubles = set()
+ # We need to check if any edges are used twice location based.
+ for ed in edges:
+ edkey = ed_key_mlen(verts[ed[0]], verts[ed[1]])
+ if edkey in edges_used:
+ edges_doubles.add(edkey)
+ else:
+ edges_used.add(edkey)
+
+ # Store a list of unconnected loop segments split by double edges.
+ # will join later
+ loop_segments = []
+
+ v_prev = verts[0]
+ context_loop = [v_prev]
+ loop_segments = [context_loop]
+
+ for v in verts:
+ if v != v_prev:
+ # Are we crossing an edge we removed?
+ if ed_key_mlen(v, v_prev) in edges_doubles:
+ context_loop = [v]
+ loop_segments.append(context_loop)
+ else:
+ if context_loop and context_loop[-1][1] == v[1]:
+ #raise "as"
+ pass
+ else:
+ context_loop.append(v)
+
+ v_prev = v
+ # Now join loop segments
+
+ def join_seg(s1, s2):
+ if s2[-1][1] == s1[0][1]:
+ s1, s2 = s2, s1
+ elif s1[-1][1] == s2[0][1]:
+ pass
+ else:
+ return False
+
+ # If were stuill here s1 and s2 are 2 segments in the same polyline
+ s1.pop() # remove the last vert from s1
+ s1.extend(s2) # add segment 2 to segment 1
+
+ if s1[0][1] == s1[-1][1]: # remove endpoints double
+ s1.pop()
+
+ s2[:] = [] # Empty this segment s2 so we dont use it again.
+ return True
+
+ joining_segments = True
+ while joining_segments:
+ joining_segments = False
+ segcount = len(loop_segments)
+
+ for j in range(segcount - 1, -1, -1): # reversed(range(segcount)):
+ seg_j = loop_segments[j]
+ if seg_j:
+ for k in range(j - 1, -1, -1): # reversed(range(j)):
+ if not seg_j:
+ break
+ seg_k = loop_segments[k]
+
+ if seg_k and join_seg(seg_j, seg_k):
+ joining_segments = True
+
+ loop_list = loop_segments
+
+ for verts in loop_list:
+ while verts and verts[0][1] == verts[-1][1]:
+ verts.pop()
+
+ loop_list = [verts for verts in loop_list if len(verts) > 2]
+ # DONE DEALING WITH LOOP FIXING
+
+ # vert mapping
+ vert_map = [None] * len(indices)
+ ii = 0
+ for verts in loop_list:
+ if len(verts) > 2:
+ for i, vert in enumerate(verts):
+ vert_map[i + ii] = vert[2]
+ ii += len(verts)
+
+ fill = tesselate_polygon([[v[0] for v in loop] for loop in loop_list])
+ #draw_loops(loop_list)
+ #raise 'done loop'
+ # map to original indices
+ fill = [[vert_map[i] for i in reversed(f)] for f in fill]
+
+ if not fill:
+ print('Warning Cannot scanfill, fallback on a triangle fan.')
+ fill = [[0, i - 1, i] for i in range(2, len(indices))]
+ else:
+ # Use real scanfill.
+ # See if its flipped the wrong way.
+ flip = None
+ for fi in fill:
+ if flip != None:
+ break
+ for i, vi in enumerate(fi):
+ if vi == 0 and fi[i - 1] == 1:
+ flip = False
+ break
+ elif vi == 1 and fi[i - 1] == 0:
+ flip = True
+ break
+
+ if not flip:
+ for i, fi in enumerate(fill):
+ fill[i] = tuple([ii for ii in reversed(fi)])
+
+ return fill
+
+
+def face_random_points(num_points, faces):
+ """
+ Generates a list of random points over mesh faces.
+
+ :arg num_points: the number of random points to generate on each face.
+ :type int:
+ :arg faces: list of the faces to generate points on.
+ :type faces: :class:`MeshFaces`, sequence
+ :return: list of random points over all faces.
+ :rtype: list
+ """
+
+ from random import random
+ from mathutils.geometry import area_tri
+
+ # Split all quads into 2 tris, tris remain unchanged
+ tri_faces = []
+ for f in faces:
+ tris = []
+ verts = f.id_data.vertices
+ fv = f.vertices[:]
+ tris.append((verts[fv[0]].co,
+ verts[fv[1]].co,
+ verts[fv[2]].co,
+ ))
+ if len(fv) == 4:
+ tris.append((verts[fv[0]].co,
+ verts[fv[3]].co,
+ verts[fv[2]].co,
+ ))
+ tri_faces.append(tris)
+
+ # For each face, generate the required number of random points
+ sampled_points = [None] * (num_points * len(faces))
+ for i, tf in enumerate(tri_faces):
+ for k in range(num_points):
+ # If this is a quad, we need to weight its 2 tris by their area
+ if len(tf) != 1:
+ area1 = area_tri(*tf[0])
+ area2 = area_tri(*tf[1])
+ area_tot = area1 + area2
+
+ area1 = area1 / area_tot
+ area2 = area2 / area_tot
+
+ vecs = tf[0 if (random() < area1) else 1]
+ else:
+ vecs = tf[0]
+
+ u1 = random()
+ u2 = random()
+ u_tot = u1 + u2
+
+ if u_tot > 1:
+ u1 = 1.0 - u1
+ u2 = 1.0 - u2
+
+ side1 = vecs[1] - vecs[0]
+ side2 = vecs[2] - vecs[0]
+
+ p = vecs[0] + u1 * side1 + u2 * side2
+
+ sampled_points[num_points * i + k] = p
+
+ return sampled_points
diff --git a/release/scripts/modules/add_object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py
index 1cf7fc2f4d5..51a8d4b5e23 100644
--- a/release/scripts/modules/add_object_utils.py
+++ b/release/scripts/modules/bpy_extras/object_utils.py
@@ -18,11 +18,27 @@
# <pep8 compliant>
+__all__ = (
+ "add_object_align_init",
+ "object_data_add",
+)
+
+
import bpy
import mathutils
def add_object_align_init(context, operator):
+ """
+ Return a matrix using the operator settings and view context.
+
+ :arg context: The context to use.
+ :type context: :class:`Context`
+ :arg operator: The operator, checked for location and rotation properties.
+ :type operator: :class:`Operator`
+ :return: the matrix from the context and settings.
+ :rtype: :class:`Matrix`
+ """
space_data = context.space_data
if space_data.type != 'VIEW_3D':
space_data = None
@@ -64,7 +80,19 @@ def add_object_align_init(context, operator):
def object_data_add(context, obdata, operator=None):
-
+ """
+ Add an object using the view context and preference to to initialize the
+ location, rotation and layer.
+
+ :arg context: The context to use.
+ :type context: :class:`Context`
+ :arg obdata: the data used for the new object.
+ :type obdata: valid object data type or None.
+ :arg operator: The operator, checked for location and rotation properties.
+ :type operator: :class:`Operator`
+ :return: the newly created object in the scene.
+ :rtype: :class:`ObjectBase`
+ """
scene = context.scene
# ugh, could be made nicer
diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py
new file mode 100644
index 00000000000..5796abce72c
--- /dev/null
+++ b/release/scripts/modules/bpy_extras/view3d_utils.py
@@ -0,0 +1,128 @@
+# ##### 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>
+
+__all__ = (
+ "region_2d_to_vector_3d",
+ "region_2d_to_location_3d",
+ "location_3d_to_region_2d",
+)
+
+
+def region_2d_to_vector_3d(region, rv3d, coord):
+ """
+ Return a direction vector from the viewport at the spesific 2d region
+ coordinate.
+
+ :arg region: region of the 3D viewport, typically bpy.context.region.
+ :type region: :class:`Region`
+ :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d.
+ :type rv3d: :class:`RegionView3D`
+ :arg coord: 2d coordinates relative to the region:
+ (event.mouse_region_x, event.mouse_region_y) for example.
+ :type coord: 2d vector
+ :return: normalized 3d vector.
+ :rtype: :class:`Vector`
+ """
+ from mathutils import Vector
+
+ if rv3d.is_perspective:
+ persinv = rv3d.perspective_matrix.inverted()
+
+ out = Vector(((2.0 * coord[0] / region.width) - 1.0,
+ (2.0 * coord[1] / region.height) - 1.0,
+ -0.5
+ ))
+
+ w = ((out[0] * persinv[0][3]) +
+ (out[1] * persinv[1][3]) +
+ (out[2] * persinv[2][3]) + persinv[3][3])
+
+ return ((persinv * out) / w) - rv3d.view_matrix.inverted()[3].xyz
+ else:
+ return rv3d.view_matrix.inverted()[2].xyz.normalized()
+
+
+def region_2d_to_location_3d(region, rv3d, coord, depth_location):
+ """
+ Return a 3d location from the region relative 2d coords, aligned with
+ *depth_location*.
+
+ :arg region: region of the 3D viewport, typically bpy.context.region.
+ :type region: :class:`Region`
+ :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d.
+ :type rv3d: :class:`RegionView3D`
+ :arg coord: 2d coordinates relative to the region;
+ (event.mouse_region_x, event.mouse_region_y) for example.
+ :type coord: 2d vector
+ :arg depth_location: the returned vectors depth is aligned with this since
+ there is no defined depth with a 2d region input.
+ :type depth_location: 3d vector
+ :return: normalized 3d vector.
+ :rtype: :class:`Vector`
+ """
+ from mathutils import Vector
+ from mathutils.geometry import intersect_point_line
+
+ persmat = rv3d.perspective_matrix.copy()
+ coord_vec = region_2d_to_vector_3d(region, rv3d, coord)
+ depth_location = Vector(depth_location)
+
+ if rv3d.is_perspective:
+ from mathutils.geometry import intersect_line_plane
+
+ origin_start = rv3d.view_matrix.inverted()[3].to_3d()
+ origin_end = origin_start + coord_vec
+ view_vec = rv3d.view_matrix.inverted()[2]
+ return intersect_line_plane(origin_start, origin_end, depth_location, view_vec, 1)
+ else:
+ dx = (2.0 * coord[0] / region.width) - 1.0
+ dy = (2.0 * coord[1] / region.height) - 1.0
+ persinv = persmat.inverted()
+ viewinv = rv3d.view_matrix.inverted()
+ origin_start = (persinv[0].xyz * dx) + (persinv[1].xyz * dy) + viewinv[3].xyz
+ origin_end = origin_start + coord_vec
+ return intersect_point_line(depth_location, origin_start, origin_end)[0]
+
+
+def location_3d_to_region_2d(region, rv3d, coord):
+ """
+ Return the *region* relative 2d location of a 3d position.
+
+ :arg region: region of the 3D viewport, typically bpy.context.region.
+ :type region: :class:`Region`
+ :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d.
+ :type rv3d: :class:`RegionView3D`
+ :arg coord: 3d worldspace location.
+ :type coord: 3d vector
+ :return: 2d location
+ :rtype: :class:`Vector`
+ """
+ from mathutils import Vector
+
+ prj = rv3d.perspective_matrix * Vector((coord[0], coord[1], coord[2], 1.0))
+ if prj.w > 0.0:
+ width_half = region.width / 2.0
+ height_half = region.height / 2.0
+
+ return Vector((width_half + width_half * (prj.x / prj.w),
+ height_half + height_half * (prj.y / prj.w),
+ ))
+ else:
+ return None
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index c3352dd33ad..8766c873dd8 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -20,7 +20,6 @@
from _bpy import types as bpy_types
import _bpy
-from mathutils import Vector
StructRNA = bpy_types.Struct.__bases__[0]
StructMetaPropGroup = _bpy.StructMetaPropGroup
@@ -144,19 +143,22 @@ class _GenericBone:
def x_axis(self):
""" Vector pointing down the x-axis of the bone.
"""
- return Vector((1.0, 0.0, 0.0)) * self.matrix.to_3x3()
+ from mathutils import Vector
+ return self.matrix.to_3x3() * Vector((1.0, 0.0, 0.0))
@property
def y_axis(self):
""" Vector pointing down the x-axis of the bone.
"""
- return Vector((0.0, 1.0, 0.0)) * self.matrix.to_3x3()
+ from mathutils import Vector
+ return self.matrix.to_3x3() * Vector((0.0, 1.0, 0.0))
@property
def z_axis(self):
""" Vector pointing down the x-axis of the bone.
"""
- return Vector((0.0, 0.0, 1.0)) * self.matrix.to_3x3()
+ from mathutils import Vector
+ return self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
@property
def basename(self):
@@ -239,7 +241,7 @@ class _GenericBone:
chain.append(child)
else:
if len(children_basename):
- print("multiple basenames found, this is probably not what you want!", bone.name, children_basename)
+ print("multiple basenames found, this is probably not what you want!", self.name, children_basename)
break
@@ -292,9 +294,9 @@ class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
:type roll: bool
"""
from mathutils import Vector
- z_vec = Vector((0.0, 0.0, 1.0)) * self.matrix.to_3x3()
- self.tail = self.tail * matrix
- self.head = self.head * matrix
+ z_vec = self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
+ self.tail = matrix * self.tail
+ self.head = matrix * self.head
if scale:
scalar = matrix.median_scale
@@ -302,7 +304,7 @@ class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
self.tail_radius *= scalar
if roll:
- self.align_roll(z_vec * matrix)
+ self.align_roll(matrix * z_vec)
def ord_ind(i1, i2):
@@ -356,163 +358,6 @@ class Mesh(bpy_types.ID):
def edge_keys(self):
return [edge_key for face in self.faces for edge_key in face.edge_keys]
- @property
- def edge_face_count_dict(self):
- face_edge_keys = [face.edge_keys for face in self.faces]
- face_edge_count = {}
- for face_keys in face_edge_keys:
- for key in face_keys:
- try:
- face_edge_count[key] += 1
- except:
- face_edge_count[key] = 1
-
- return face_edge_count
-
- @property
- def edge_face_count(self):
- edge_face_count_dict = self.edge_face_count_dict
- return [edge_face_count_dict.get(ed.key, 0) for ed in self.edges]
-
- def edge_loops_from_faces(self, faces=None, seams=()):
- """
- Edge loops defined by faces
-
- Takes me.faces or a list of faces and returns the edge loops
- These edge loops are the edges that sit between quads, so they dont touch
- 1 quad, note: not connected will make 2 edge loops, both only containing 2 edges.
-
- return a list of edge key lists
- [ [(0,1), (4, 8), (3,8)], ...]
-
- return a list of edge vertex index lists
- """
-
- OTHER_INDEX = 2, 3, 0, 1 # opposite face index
-
- if faces is None:
- faces = self.faces
-
- edges = {}
-
- for f in faces:
-# if len(f) == 4:
- if f.vertices_raw[3] != 0:
- edge_keys = f.edge_keys
- for i, edkey in enumerate(f.edge_keys):
- edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]])
-
- for edkey in seams:
- edges[edkey] = []
-
- # Collect edge loops here
- edge_loops = []
-
- for edkey, ed_adj in edges.items():
- if 0 < len(ed_adj) < 3: # 1 or 2
- # Seek the first edge
- context_loop = [edkey, ed_adj[0]]
- edge_loops.append(context_loop)
- if len(ed_adj) == 2:
- other_dir = ed_adj[1]
- else:
- other_dir = None
-
- ed_adj[:] = []
-
- flipped = False
-
- while 1:
- # from knowing the last 2, look for th next.
- ed_adj = edges[context_loop[-1]]
- if len(ed_adj) != 2:
-
- if other_dir and flipped == False: # the original edge had 2 other edges
- flipped = True # only flip the list once
- context_loop.reverse()
- ed_adj[:] = []
- context_loop.append(other_dir) # save 1 lookiup
-
- ed_adj = edges[context_loop[-1]]
- if len(ed_adj) != 2:
- ed_adj[:] = []
- break
- else:
- ed_adj[:] = []
- break
-
- i = ed_adj.index(context_loop[-2])
- context_loop.append(ed_adj[not i])
-
- # Dont look at this again
- ed_adj[:] = []
-
- return edge_loops
-
- def edge_loops_from_edges(self, edges=None):
- """
- Edge loops defined by edges
-
- Takes me.edges or a list of edges and returns the edge loops
-
- return a list of vertex indices.
- [ [1, 6, 7, 2], ...]
-
- closed loops have matching start and end values.
- """
- line_polys = []
-
- # Get edges not used by a face
- if edges is None:
- edges = self.edges
-
- if not hasattr(edges, "pop"):
- edges = edges[:]
-
- edge_dict = {ed.key: ed for ed in self.edges if ed.select}
-
- while edges:
- current_edge = edges.pop()
- vert_end, vert_start = current_edge.vertices[:]
- line_poly = [vert_start, vert_end]
-
- ok = True
- while ok:
- ok = False
- #for i, ed in enumerate(edges):
- i = len(edges)
- while i:
- i -= 1
- ed = edges[i]
- v1, v2 = ed.vertices
- if v1 == vert_end:
- line_poly.append(v2)
- vert_end = line_poly[-1]
- ok = 1
- del edges[i]
- # break
- elif v2 == vert_end:
- line_poly.append(v1)
- vert_end = line_poly[-1]
- ok = 1
- del edges[i]
- #break
- elif v1 == vert_start:
- line_poly.insert(0, v2)
- vert_start = line_poly[0]
- ok = 1
- del edges[i]
- # break
- elif v2 == vert_start:
- line_poly.insert(0, v1)
- vert_start = line_poly[0]
- ok = 1
- del edges[i]
- #break
- line_polys.append(line_poly)
-
- return line_polys
-
class MeshEdge(StructRNA):
__slots__ = ()
diff --git a/release/scripts/modules/bpyml_ui.py b/release/scripts/modules/bpyml_ui.py
index 1e0522974d1..5df04b8bf34 100644
--- a/release/scripts/modules/bpyml_ui.py
+++ b/release/scripts/modules/bpyml_ui.py
@@ -22,7 +22,6 @@
import bpy as _bpy
import bpyml
from bpyml import TAG, ARGS, CHILDREN
-from types import ModuleType
_uilayout_rna = _bpy.types.UILayout.bl_rna
diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py
index a31280ebff0..d787fed0967 100644
--- a/release/scripts/modules/console/complete_namespace.py
+++ b/release/scripts/modules/console/complete_namespace.py
@@ -37,6 +37,11 @@ def is_dict(obj):
return hasattr(obj, 'keys') and hasattr(getattr(obj, 'keys'), '__call__')
+def is_struct_seq(obj):
+ """Returns whether obj is a structured sequence subclass: sys.float_info"""
+ return isinstance(obj, tuple) and hasattr(obj, 'n_fields')
+
+
def complete_names(word, namespace):
"""Complete variable names or attributes
@@ -174,7 +179,7 @@ def complete(word, namespace, private=True):
if type(obj) in (bool, float, int, str):
return []
# an extra char '[', '(' or '.' will be added
- if hasattr(obj, '__getitem__'):
+ if hasattr(obj, '__getitem__') and not is_struct_seq(obj):
# list or dictionary
matches = complete_indices(word, namespace, obj)
elif hasattr(obj, '__call__'):
diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py
index 9352d7c14e1..a177b305fda 100644
--- a/release/scripts/modules/console/intellisense.py
+++ b/release/scripts/modules/console/intellisense.py
@@ -53,7 +53,7 @@ RE_UNQUOTED_WORD = re.compile(
re.UNICODE)
-def complete(line, cursor, namespace, private=True):
+def complete(line, cursor, namespace, private):
"""Returns a list of possible completions:
* name completion
@@ -82,6 +82,9 @@ def complete(line, cursor, namespace, private=True):
if RE_MODULE.match(line):
from . import complete_import
matches = complete_import.complete(line)
+ if not private:
+ matches[:] = [m for m in matches if m[:1] != "_"]
+ matches.sort()
else:
from . import complete_namespace
matches = complete_namespace.complete(word, namespace, private)
@@ -120,15 +123,29 @@ def expand(line, cursor, namespace, private=True):
from . import complete_calltip
matches, word, scrollback = complete_calltip.complete(line,
cursor, namespace)
+ prefix = os.path.commonprefix(matches)[len(word):]
no_calltip = False
else:
matches, word = complete(line, cursor, namespace, private)
+ prefix = os.path.commonprefix(matches)[len(word):]
if len(matches) == 1:
scrollback = ''
else:
- scrollback = ' '.join([m.split('.')[-1] for m in matches])
+ # causes blender bug [#27495] since string keys may contain '.'
+ # scrollback = ' '.join([m.split('.')[-1] for m in matches])
+
+ # add white space to align with the cursor
+ white_space = " " + (" " * (cursor + len(prefix)))
+ word_prefix = word + prefix
+ scrollback = '\n'.join(
+ [white_space + m[len(word_prefix):]
+ if (word_prefix and m.startswith(word_prefix))
+ else
+ white_space + m.split('.')[-1]
+ for m in matches])
+
no_calltip = True
- prefix = os.path.commonprefix(matches)[len(word):]
+
if prefix:
line = line[:cursor] + prefix + line[cursor:]
cursor += len(prefix)
diff --git a/release/scripts/modules/console_python.py b/release/scripts/modules/console_python.py
index 3048fa1d597..455eabe377b 100644
--- a/release/scripts/modules/console_python.py
+++ b/release/scripts/modules/console_python.py
@@ -80,7 +80,7 @@ def get_console(console_id):
if console_data:
console, stdout, stderr = console_data
- # XXX, bug in python 3.1.2 ? (worked in 3.1.1)
+ # XXX, bug in python 3.1.2, 3.2 ? (worked in 3.1.1)
# seems there is no way to clear StringIO objects for writing, have to make new ones each time.
import io
stdout = io.StringIO()
diff --git a/release/scripts/modules/io_utils.py b/release/scripts/modules/io_utils.py
deleted file mode 100644
index 4fcfcfbd0a9..00000000000
--- a/release/scripts/modules/io_utils.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# ##### 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.props import StringProperty, BoolProperty, EnumProperty
-
-
-class ExportHelper:
- filepath = StringProperty(name="File Path", description="Filepath used for exporting the file", maxlen=1024, default="", subtype='FILE_PATH')
- check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
-
- # subclasses can override with decorator
- # True == use ext, False == no ext, None == do nothing.
- check_extension = True
-
- def invoke(self, context, event):
- import os
- if not self.filepath:
- blend_filepath = context.blend_data.filepath
- if not blend_filepath:
- blend_filepath = "untitled"
- else:
- blend_filepath = os.path.splitext(blend_filepath)[0]
-
- self.filepath = blend_filepath + self.filename_ext
-
- context.window_manager.fileselect_add(self)
- return {'RUNNING_MODAL'}
-
- def check(self, context):
- check_extension = self.check_extension
-
- if check_extension is None:
- return False
-
- filepath = bpy.path.ensure_ext(self.filepath, self.filename_ext if check_extension else "")
-
- if filepath != self.filepath:
- self.filepath = filepath
- return True
-
- return False
-
-
-class ImportHelper:
- filepath = StringProperty(name="File Path", description="Filepath used for importing the file", maxlen=1024, default="", subtype='FILE_PATH')
-
- def invoke(self, context, event):
- context.window_manager.fileselect_add(self)
- return {'RUNNING_MODAL'}
-
-
-# limited replacement for BPyImage.comprehensiveImageLoad
-def load_image(imagepath, dirname):
- import os
-
- if os.path.exists(imagepath):
- return bpy.data.images.load(imagepath)
-
- variants = [imagepath, os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))]
-
- for filepath in variants:
- for nfilepath in (filepath, bpy.path.resolve_ncase(filepath)):
- if os.path.exists(nfilepath):
- return bpy.data.images.load(nfilepath)
-
- # TODO comprehensiveImageLoad also searched in bpy.config.textureDir
- return None
-
-
-# return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects()
-def create_derived_objects(scene, ob):
- if ob.parent and ob.parent.dupli_type != 'NONE':
- return False, None
-
- if ob.dupli_type != 'NONE':
- ob.dupli_list_create(scene)
- return True, [(dob.object, dob.matrix) for dob in ob.dupli_list]
- else:
- return False, [(ob, ob.matrix_world)]
-
-
-def free_derived_objects(ob):
- ob.dupli_list_clear()
-
-
-def unpack_list(list_of_tuples):
- flat_list = []
- flat_list_extend = flat_list.extend # a tich faster
- for t in list_of_tuples:
- flat_list_extend(t)
- return flat_list
-
-
-# same as above except that it adds 0 for triangle faces
-def unpack_face_list(list_of_tuples):
- #allocate the entire list
- flat_ls = [0] * (len(list_of_tuples) * 4)
- i = 0
-
- for t in list_of_tuples:
- if len(t) == 3:
- if t[2] == 0:
- t = t[1], t[2], t[0]
- else: # assuem quad
- if t[3] == 0 or t[2] == 0:
- t = t[2], t[3], t[0], t[1]
-
- flat_ls[i:i + len(t)] = t
- i += 4
- return flat_ls
-
-
-path_reference_mode = EnumProperty(
- name="Path Mode",
- description="Method used to reference paths",
- items=(('AUTO', "Auto", "Use Relative paths with subdirectories only"),
- ('ABSOLUTE', "Absolute", "Always write absolute paths"),
- ('RELATIVE', "Relative", "Always write relative patsh (where possible)"),
- ('MATCH', "Match", "Match Absolute/Relative setting with input path"),
- ('STRIP', "Strip Path", "Filename only"),
- ('COPY', "Copy", "copy the file to the destination path (or subdirectory)"),
- ),
- default='AUTO'
- )
-
-
-def path_reference(filepath, base_src, base_dst, mode='AUTO', copy_subdir="", copy_set=None):
- """
- Return a filepath relative to a destination directory, for use with
- exporters.
-
- :arg filepath: the file path to return, supporting blenders relative '//' prefix.
- :type filepath: string
- :arg base_src: the directory the *filepath* is relative too (normally the blend file).
- :type base_src: string
- :arg base_dst: the directory the *filepath* will be referenced from (normally the export path).
- :type base_dst: string
- :arg mode: the method used get the path in ['AUTO', 'ABSOLUTE', 'RELATIVE', 'MATCH', 'STRIP', 'COPY']
- :type mode: string
- :arg copy_subdir: the subdirectory of *base_dst* to use when mode='COPY'.
- :type copy_subdir: string
- :arg copy_set: collect from/to pairs when mode='COPY', pass to *path_reference_copy* when exportign is done.
- :type copy_set: set
- :return: the new filepath.
- :rtype: string
- """
- import os
- is_relative = filepath.startswith("//")
- filepath_abs = os.path.normpath(bpy.path.abspath(filepath, base_src))
-
- if mode in ('ABSOLUTE', 'RELATIVE', 'STRIP'):
- pass
- elif mode == 'MATCH':
- mode = 'RELATIVE' if is_relative else 'ABSOLUTE'
- elif mode == 'AUTO':
- mode = 'RELATIVE' if bpy.path.is_subdir(filepath, base_dst) else 'ABSOLUTE'
- elif mode == 'COPY':
- if copy_subdir:
- subdir_abs = os.path.join(os.path.normpath(base_dst), copy_subdir)
- else:
- subdir_abs = os.path.normpath(base_dst)
-
- filepath_cpy = os.path.join(subdir_abs, os.path.basename(filepath))
-
- copy_set.add((filepath_abs, filepath_cpy))
-
- filepath_abs = filepath_cpy
- mode = 'RELATIVE'
- else:
- Excaption("invalid mode given %r" % mode)
-
- if mode == 'ABSOLUTE':
- return filepath_abs
- elif mode == 'RELATIVE':
- return os.path.relpath(filepath_abs, base_dst)
- elif mode == 'STRIP':
- return os.path.basename(filepath_abs)
-
-
-def path_reference_copy(copy_set, report=print):
- """
- Execute copying files of path_reference
-
- :arg copy_set: set of (from, to) pairs to copy.
- :type copy_set: set
- :arg report: function used for reporting warnings, takes a string argument.
- :type report: function
- """
- if not copy_set:
- return
-
- import os
- import shutil
-
- for file_src, file_dst in copy_set:
- if not os.path.exists(file_src):
- report("missing %r, not copying" % file_src)
- elif os.path.exists(file_dst) and os.path.samefile(file_src, file_dst):
- pass
- else:
- dir_to = os.path.dirname(file_dst)
-
- if not os.path.isdir(dir_to):
- os.makedirs(dir_to)
-
- shutil.copy(file_src, file_dst)
diff --git a/release/scripts/modules/mesh_utils.py b/release/scripts/modules/mesh_utils.py
deleted file mode 100644
index 5bacff7b0cc..00000000000
--- a/release/scripts/modules/mesh_utils.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# ##### 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>
-
-
-def mesh_linked_faces(mesh):
- '''
- Splits the mesh into connected parts,
- these parts are returned as lists of faces.
- used for seperating cubes from other mesh elements in the 1 mesh
- '''
-
- # Build vert face connectivity
- vert_faces = [[] for i in range(len(mesh.vertices))]
- for f in mesh.faces:
- for v in f.vertices:
- vert_faces[v].append(f)
-
- # sort faces into connectivity groups
- face_groups = [[f] for f in mesh.faces]
- face_mapping = list(range(len(mesh.faces))) # map old, new face location
-
- # Now clump faces iterativly
- ok = True
- while ok:
- ok = False
-
- for i, f in enumerate(mesh.faces):
- mapped_index = face_mapping[f.index]
- mapped_group = face_groups[mapped_index]
-
- for v in f.vertices:
- for nxt_f in vert_faces[v]:
- if nxt_f != f:
- nxt_mapped_index = face_mapping[nxt_f.index]
-
- # We are not a part of the same group
- if mapped_index != nxt_mapped_index:
- ok = True
-
- # Assign mapping to this group so they all map to this group
- for grp_f in face_groups[nxt_mapped_index]:
- face_mapping[grp_f.index] = mapped_index
-
- # Move faces into this group
- mapped_group.extend(face_groups[nxt_mapped_index])
-
- # remove reference to the list
- face_groups[nxt_mapped_index] = None
-
- # return all face groups that are not null
- # this is all the faces that are connected in their own lists.
- return [fg for fg in face_groups if fg]
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index 9311987e2e7..388ae2b0e13 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -111,12 +111,16 @@ def draw(layout, context, context_member, property_type, use_edit=True):
continue
row = layout.row()
- convert_to_pyobject = getattr(val, "convert_to_pyobject", None)
-
- val_orig = val
- if convert_to_pyobject:
- val_draw = val = val.convert_to_pyobject()
- val_draw = str(val_draw)
+ to_dict = getattr(val, "to_dict", None)
+ to_list = getattr(val, "to_list", None)
+
+ # val_orig = val # UNUSED
+ if to_dict:
+ val = to_dict()
+ val_draw = str(val)
+ elif to_list:
+ val = to_list()
+ val_draw = str(val)
else:
val_draw = val
@@ -131,7 +135,7 @@ def draw(layout, context, context_member, property_type, use_edit=True):
row.label(text=key)
# explicit exception for arrays
- if convert_to_pyobject and not hasattr(val_orig, "len"):
+ if to_dict or to_list:
row.label(text=val_draw)
else:
if key in rna_properties:
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")
diff --git a/release/scripts/templates/batch_export.py b/release/scripts/templates/batch_export.py
new file mode 100644
index 00000000000..45d26f4b525
--- /dev/null
+++ b/release/scripts/templates/batch_export.py
@@ -0,0 +1,33 @@
+# exports each selected object into its own file
+
+import bpy
+import os
+
+# export to blend file location
+basedir = os.path.dirname(bpy.data.filepath)
+
+if not basedir:
+ raise Exception("Blend file is not saved")
+
+selection = bpy.context.selected_objects
+
+bpy.ops.object.select_all(action='DESELECT')
+
+for obj in selection:
+
+ obj.select = True
+
+ name = bpy.path.clean_name(obj.name)
+ fn = os.path.join(basedir, name)
+
+ bpy.ops.export_scene.fbx(filepath=fn + ".fbx", use_selection=True)
+
+ ## Can be used for multiple formats
+ # bpy.ops.export_scene.x3d(filepath=fn + ".x3d", use_selection=True)
+
+ obj.select = False
+
+ print("written:", fn)
+
+for obj in selection:
+ obj.select = True
diff --git a/release/scripts/templates/operator_export.py b/release/scripts/templates/operator_export.py
index 1b1c90c8a21..4cf943a53b7 100644
--- a/release/scripts/templates/operator_export.py
+++ b/release/scripts/templates/operator_export.py
@@ -12,7 +12,7 @@ def write_some_data(context, filepath, use_some_setting):
# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
-from io_utils import ExportHelper
+from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
diff --git a/release/scripts/templates/operator_mesh_add.py b/release/scripts/templates/operator_mesh_add.py
index 65b08eebb4e..10d23a6712d 100644
--- a/release/scripts/templates/operator_mesh_add.py
+++ b/release/scripts/templates/operator_mesh_add.py
@@ -80,8 +80,8 @@ class AddBox(bpy.types.Operator):
mesh.update()
# add the mesh as an object into the scene with this utility module
- 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/templates/ui_menu.py b/release/scripts/templates/ui_menu.py
new file mode 100644
index 00000000000..d3c94b86809
--- /dev/null
+++ b/release/scripts/templates/ui_menu.py
@@ -0,0 +1,49 @@
+import bpy
+
+
+class CustomMenu(bpy.types.Menu):
+ bl_label = "Custom Menu"
+ bl_idname = "OBJECT_MT_custom_menu"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("wm.open_mainfile")
+ layout.operator("wm.save_as_mainfile").copy = True
+
+ layout.operator("object.shade_smooth")
+
+ layout.label(text="Hello world!", icon='WORLD_DATA')
+
+ # use an operator enum property to populate a submenu
+ layout.operator_menu_enum("object.select_by_type",
+ property="type",
+ text="Select All by Type...",
+ )
+
+ # call another menu
+ layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
+
+
+def draw_item(self, context):
+ layout = self.layout
+ layout.menu(CustomMenu.bl_idname)
+
+
+def register():
+ bpy.utils.register_class(CustomMenu)
+
+ # lets add ourselves to the main header
+ bpy.types.INFO_HT_header.append(draw_item)
+
+
+def unregister():
+ bpy.utils.unregister_class(CustomMenu)
+
+ bpy.types.INFO_HT_header.remove(draw_item)
+
+if __name__ == "__main__":
+ register()
+
+ # The menu can also be called from scripts
+ bpy.ops.wm.call_menu(name=CustomMenu.bl_idname)
diff --git a/release/scripts/templates/ui_menu_simple.py b/release/scripts/templates/ui_menu_simple.py
new file mode 100644
index 00000000000..2129dfd81a4
--- /dev/null
+++ b/release/scripts/templates/ui_menu_simple.py
@@ -0,0 +1,26 @@
+import bpy
+
+
+class SimpleCustomMenu(bpy.types.Menu):
+ bl_label = "Simple Custom Menu"
+ bl_idname = "OBJECT_MT_simple_custom_menu"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("wm.open_mainfile")
+ layout.operator("wm.save_as_mainfile")
+
+
+def register():
+ bpy.utils.register_class(SimpleCustomMenu)
+
+
+def unregister():
+ bpy.utils.unregister_class(SimpleCustomMenu)
+
+if __name__ == "__main__":
+ register()
+
+ # The menu can also be called from scripts
+ bpy.ops.wm.call_menu(name=SimpleCustomMenu.bl_idname)
diff --git a/release/scripts/templates/panel_simple.py b/release/scripts/templates/ui_panel_simple.py
index e5bf70cb654..cde6126b626 100644
--- a/release/scripts/templates/panel_simple.py
+++ b/release/scripts/templates/ui_panel_simple.py
@@ -1,8 +1,9 @@
import bpy
-class OBJECT_PT_hello(bpy.types.Panel):
+class HelloWorldPanel(bpy.types.Panel):
bl_label = "Hello World Panel"
+ bl_idname = "OBJECT_PT_hello"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
@@ -22,11 +23,11 @@ class OBJECT_PT_hello(bpy.types.Panel):
def register():
- bpy.utils.register_class(OBJECT_PT_hello)
+ bpy.utils.register_class(HelloWorldPanel)
def unregister():
- bpy.utils.unregister_class(OBJECT_PT_hello)
+ bpy.utils.unregister_class(HelloWorldPanel)
if __name__ == "__main__":
diff --git a/release/text/GPL-license.txt b/release/text/GPL-license.txt
index 8860b2a8afa..e8c0353e734 100644
--- a/release/text/GPL-license.txt
+++ b/release/text/GPL-license.txt
@@ -2,7 +2,7 @@
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
diff --git a/release/text/readme.html b/release/text/readme.html
index 1014d8ef80a..2b5a4071a7f 100644
--- a/release/text/readme.html
+++ b/release/text/readme.html
@@ -12,22 +12,22 @@
</style>
</head>
<body>
-<p class="title"><b>Blender 2.57</b></p>
+<p class="title"><b>Blender 2.58</b></p>
<p><br></p>
<p class="header"><b>About</b></p>
<p class="body">Welcome to Blender, the free, open source 3D application for modeling, animation, rendering, compositing, video editing and game creation. Blender is available for Linux, Mac OS X, Windows, Solaris and FreeBSD and has a large world-wide community.</p>
<p class="body">Blender can be used freely for any purpose, including commercial use and distribution. It's free and open-source software, released under the GNU GPL licence. The entire source code is available on our website.</p>
<p class="body">For more information, visit <a href="http://www.blender.org">blender.org</a>.</p>
<p><br></p>
-<p class="header"><b>2.57</b></p>
-<p class="body">The Blender Foundation and online developer community is proud to present Blender 2.57. This release is the first official stable release of the Blender 2.5 series, and represents the culmination of many years of redesign and development work. <a href="http://www.blender.org/development/release-logs/blender-256-beta/">More information about this release</a>.</p>
+<p class="header"><b>2.58</b></p>
+<p class="body">The Blender Foundation and online developer community is proud to present Blender 2.58. This release is the second official stable release of the Blender 2.5 series, and represents the culmination of many years of redesign and development work. <a href="http://www.blender.org/development/release-logs/blender-258/">More information about this release</a>.</p>
<p class="body">What to Expect:</p>
<p class="body"> • Big improvements - This is our most exciting version to date, already a significant improvement in many ways over 2.49</p>
<p class="body"> • Missing/Incomplete Features - Although most of it is there, not all functionality from pre-2.5 versions has been restored yet. Some functionality may be re-implemented a different way.</p>
<p class="body"> • Changes - If you're used to the old Blenders, Blender 2.5 may seem quite different at first, but it won't be long before it grows on you even more than before.</p>
<p><br></p>
<p class="header"><b>Bugs</b></p>
-<p class="body">Although Blender 2.57 is considered a stable release, you may encounter a bug. If you do, please help us by posting it in the bug tracker or using Help → Report a Bug from inside Blender 2.57. If it wasn’t reported yet, please log in (or register) and fill in detailed information about the error. Please post detailed instructions on how to reproduce it or post a .blend file showcasing the bug.</p>
+<p class="body">Although Blender 2.58 is considered a stable release, you may encounter a bug. If you do, please help us by posting it in the bug tracker or using Help → Report a Bug from inside Blender 2.58. If it wasn’t reported yet, please log in (or register) and fill in detailed information about the error. Please post detailed instructions on how to reproduce it or post a .blend file showcasing the bug.</p>
<p><br></p>
<p class="header"><b>Package Contents</b></p>
<p class="body">The downloaded Blender package includes:</p>
@@ -51,7 +51,7 @@
<p class="header"><b>Links</b></p>
<p class="body">Users:</p>
<p class="body"> General information <a href="http://www.blender.org">www.blender.org</a> <br>
- Full release log <a href="http://www.blender.org/development/release-logs/blender-257/">www.blender.org/development/release-logs/blender-257/</a><br>
+ Full release log <a href="http://www.blender.org/development/release-logs/blender-258/">www.blender.org/development/release-logs/blender-258/</a><br>
Tutorials <a href="http://www.blender.org/education-help/">www.blender.org/education-help/</a> <br>
Manual <a href="http://wiki.blender.org/index.php/Doc:Manual">wiki.blender.org/index.php/Doc:Manual</a><br>
User Forum <a href="http://www.blenderartists.org">www.blenderartists.org</a><br>
diff --git a/release/windows/installer/00.sconsblender.nsi b/release/windows/installer/00.sconsblender.nsi
index 2a37f7f6ba6..eddd215c64d 100644
--- a/release/windows/installer/00.sconsblender.nsi
+++ b/release/windows/installer/00.sconsblender.nsi
@@ -1,261 +1,235 @@
-;
-; $Id$
-;
-; Blender Self-Installer for Windows (NSIS - http://nsis.sourceforge.net)
-;
-
-SetCompressor /SOLID lzma
-
-Name "Blender [VERSION]"
-
-RequestExecutionLevel admin
-
-!include "MUI.nsh"
-!include "WinVer.nsh"
-!include "FileFunc.nsh"
-!include "WordFunc.nsh"
-!include "nsDialogs.nsh"
-!include "x64.nsh"
-
-!define MUI_ABORTWARNING
-
-!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Blender. It is recommended that you close all other applications before starting Setup."
-!define MUI_WELCOMEFINISHPAGE_BITMAP "[RELDIR]\01.installer.bmp"
-!define MUI_HEADERIMAGE
-!define MUI_HEADERIMAGE_BITMAP "[RELDIR]\00.header.bmp"
-!define MUI_COMPONENTSPAGE_SMALLDESC
-!define MUI_FINISHPAGE_RUN "$INSTDIR\blender.exe"
-!define MUI_CHECKBITMAP "[RELDIR]\00.checked.bmp"
-!define MUI_UNWELCOMEFINISHPAGE_BITMAP "[RELDIR]\01.installer.bmp"
-
-!insertmacro MUI_PAGE_WELCOME
-!insertmacro MUI_PAGE_LICENSE "[DISTDIR]\Copyright.txt"
-!insertmacro MUI_PAGE_COMPONENTS
-
-!insertmacro MUI_PAGE_DIRECTORY
-Page custom DataLocation DataLocationOnLeave
-!insertmacro MUI_PAGE_INSTFILES
-!insertmacro MUI_PAGE_FINISH
-
-!insertmacro MUI_UNPAGE_WELCOME
-!insertmacro MUI_UNPAGE_CONFIRM
-!insertmacro MUI_UNPAGE_INSTFILES
-!insertmacro MUI_UNPAGE_FINISH
-
-!insertmacro Locate
-!insertmacro VersionCompare
-
-
-Icon "[RELDIR]\00.installer.ico"
-UninstallIcon "[RELDIR]\00.installer.ico"
-
-;--------------------------------
-;Languages
-
- !insertmacro MUI_LANGUAGE "English"
-
-;--------------------------------
-;Language Strings
-
- ;Description
- LangString DESC_InstallFiles ${LANG_ENGLISH} "Copy all required files to the application folder."
- LangString DESC_StartMenu ${LANG_ENGLISH} "Add shortcut items to the Start Menu. (Recommended)"
- LangString DESC_DesktopShortcut ${LANG_ENGLISH} "Add a shortcut to Blender on your desktop."
- LangString DESC_BlendRegister ${LANG_ENGLISH} "Blender can register itself with .blend files to allow double-clicking from Windows Explorer, etc."
- LangString TEXT_IO_TITLE ${LANG_ENGLISH} "Specify User Data Location"
-;--------------------------------
-;Data
-
-Caption "Blender [VERSION] Installer"
-OutFile "[DISTDIR]\..\blender-[VERSION]-windows[BITNESS].exe"
-InstallDir $INSTDIR ; $INSTDIR is set inside .onInit
-BrandingText "Blender Foundation | http://www.blender.org"
-ComponentText "This will install Blender [VERSION] on your computer."
-
-DirText "Use the field below to specify the folder where you want Blender to be copied to. To specify a different folder, type a new name or use the Browse button to select an existing folder."
-
-SilentUnInstall normal
-
-Var BLENDERHOME
-Var SHORTVERSION ; This is blender_version_decimal() from path_util.c
-
-; Custom controls
-Var HWND
-
-Var HWND_APPDATA
-Var HWND_INSTDIR
-Var HWND_HOMEDIR
-
-Function .onInit
- ClearErrors
- StrCpy $SHORTVERSION "[SHORTVERSION]"
-
- ${If} ${RunningX64}
- ${If} "[BITNESS]" == "32"
- ${OrIf} "[BITNESS]" == "-mingw"
- StrCpy $INSTDIR "$PROGRAMFILES32\Blender Foundation\Blender" ; Can't use InstallDir inside Section
- ${ElseIf} "[BITNESS]" == "64"
- StrCpy $INSTDIR "$PROGRAMFILES64\Blender Foundation\Blender"
- ${EndIf}
- ${Else}
- StrCpy $INSTDIR "$PROGRAMFILES\Blender Foundation\Blender"
- ${EndIf}
-FunctionEnd
-
-Function un.onInit
-FunctionEnd
-
-Function DataLocation
- nsDialogs::Create /NOUNLOAD 1018
- Pop $HWND
-
- ${If} $HWND == error
- Abort
- ${EndIf}
-
- ${NSD_CreateLabel} 0 0 100% 24u "Please specify where you wish to install Blender's user data files. Be aware that if you choose to use your Application Data directory, your preferences and scripts will only be accessible by the current user account."
- ${NSD_CreateRadioButton} 0 50 100% 12u "Use Application Data directory (recommended)"
- Pop $HWND_APPDATA
- ${NSD_CreateRadioButton} 0 80 100% 12u "Use installation directory"
- Pop $HWND_INSTDIR
- ${NSD_CreateRadioButton} 0 110 100% 12u "I have defined a %HOME% variable, please install files there"
- Pop $HWND_HOMEDIR
-
- ${If} ${AtMostWinME}
- GetDlgItem $0 $HWND $HWND_APPDATA
- EnableWindow $0 0
- SendMessage $HWND_INSTDIR ${BM_SETCHECK} 1 0
- ${Else}
- SendMessage $HWND_APPDATA ${BM_SETCHECK} 1 0
- ${EndIf}
-
- nsDialogs::Show
-
-FunctionEnd
-
-Function DataLocationOnLeave
- ${NSD_GetState} $HWND_APPDATA $R0
- ${If} $R0 == "1"
- SetShellVarContext current
- StrCpy $BLENDERHOME "$APPDATA\Blender Foundation\Blender"
- SetShellVarContext all
- ${Else}
- ${NSD_GetState} $HWND_INSTDIR $R0
- ${If} $R0 == "1"
- StrCpy $BLENDERHOME $INSTDIR
- ${Else}
- ${NSD_GetState} $HWND_HOMEDIR $R0
- ${If} $R0 == "1"
- ReadEnvStr $BLENDERHOME "HOME"
- ${EndIf}
- ${EndIf}
- ${EndIf}
-FunctionEnd
-
-Section "Blender [VERSION] (required)" InstallFiles
- SectionIn RO
-
- ; Set output path to the installation directory.
- SetOutPath $INSTDIR
- ; The contents of Blender installation root dir
- [ROOTDIRCONTS]
-
- ; All datafiles (python, scripts, config)
- [DODATAFILES]
-
- SetOutPath $INSTDIR
- ${If} ${RunningX64}
- SetRegView 64
- ${EndIf}
- ; Write the installation path into the registry
- WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "Install_Dir" "$INSTDIR"
- WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "ConfigData_Dir" "$BLENDERHOME"
- WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "ShortVersion" "[SHORTVERSION]"
- ; Write the uninstall keys for Windows
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayName" "Blender"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "Publisher" "Blender Foundation"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "URLInfoAbout" "http://www.blender.org/"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayVersion" "[VERSION]"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayIcon" "$INSTDIR\blender.exe"
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "UninstallString" '"$INSTDIR\uninstall.exe"'
- WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "NoModify" 1
- WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "NoRepair " 1
- WriteUninstaller "uninstall.exe"
-
-SectionEnd
-
-Section "Add Start Menu Shortcuts" StartMenu
- SetShellVarContext all
- CreateDirectory "$SMPROGRAMS\Blender Foundation\Blender\"
- CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
- CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Blender.lnk" "$INSTDIR\Blender.exe" "" "$INSTDIR\blender.exe" 0
- CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Readme.lnk" "$INSTDIR\readme.html" "" "" 0
- CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Copyright.lnk" "$INSTDIR\Copyright.txt" "" "$INSTDIR\copyright.txt" 0
- CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\GPL-license.lnk" "$INSTDIR\GPL-license.txt" "" "$INSTDIR\GPL-license.txt" 0
- System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' ; refresh icons
-SectionEnd
-
-Section "Add Desktop Shortcut" DesktopShortcut
- CreateShortCut "$DESKTOP\Blender.lnk" "$INSTDIR\blender.exe" "" "$INSTDIR\blender.exe" 0
- System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' ; refresh icons
-SectionEnd
-
-Section "Open .blend files with Blender" BlendRegister
- ExecWait '"$INSTDIR\blender.exe" -r'
-SectionEnd
-
-UninstallText "This will uninstall Blender [VERSION], and all installed files. Before continuing make sure you have created backup of all the files you may want to keep: startup.blend, bookmarks.txt, recent-files.txt. Hit 'Uninstall' to continue."
-
-Section "Uninstall"
- ; Remove registry keys
- ${If} ${RunningX64}
- SetRegView 64
- ${EndIf}
-
- ReadRegStr $BLENDERHOME HKLM "SOFTWARE\BlenderFoundation" "ConfigData_Dir"
- ReadRegStr $SHORTVERSION HKLM "SOFTWARE\BlenderFoundation" "ShortVersion"
- DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Blender"
- DeleteRegKey HKLM "SOFTWARE\BlenderFoundation"
- DeleteRegKey HKCR ".blend"
- DeleteRegKey HKCR "blendfile"
- DeleteRegKey HKCR "CLSID\{D45F043D-F17F-4e8a-8435-70971D9FA46D}"
- SetShellVarContext all
-
- ; Remove files
- [DELROOTDIRCONTS]
-
- Delete "$INSTDIR\uninstall.exe"
-
- MessageBox MB_YESNO "Recursively erase contents of $BLENDERHOME\$SHORTVERSION\scripts? NOTE: This includes all installed scripts and *any* file and directory you have manually created, installed later or copied. This also including .blend files." IDNO NextNoScriptRemove
- RMDir /r "$BLENDERHOME\$SHORTVERSION\scripts"
-NextNoScriptRemove:
- MessageBox MB_YESNO "Recursively erase contents from $BLENDERHOME\$SHORTVERSION\config? NOTE: This includes your startup.blend, bookmarks and any other file and directory you may have created in that directory" IDNO NextNoConfigRemove
- RMDir /r "$BLENDERHOME\$SHORTVERSION\config"
-NextNoConfigRemove:
- MessageBox MB_YESNO "Recursively erase contents from $BLENDERHOME\$SHORTVERSION\plugins? NOTE: This includes files and subdirectories in this directory" IDNO NextNoPluginRemove
- RMDir /r "$BLENDERHOME\$SHORTVERSION\plugins"
-NextNoPluginRemove:
- ; Try to remove dirs, but leave them if they contain anything
- RMDir "$BLENDERHOME\$SHORTVERSION\plugins"
- RMDir "$BLENDERHOME\$SHORTVERSION\config"
- RMDir "$BLENDERHOME\$SHORTVERSION\scripts"
- RMDir "$BLENDERHOME\$SHORTVERSION"
- RMDir "$BLENDERHOME"
- ; Remove shortcuts
- Delete "$SMPROGRAMS\Blender Foundation\Blender\*.*"
- Delete "$DESKTOP\Blender.lnk"
- ; Remove all link related directories and files
- RMDir /r "$SMPROGRAMS\Blender Foundation"
- ; Clear out installation dir
- RMDir /r "$INSTDIR"
-
- System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' ; Refresh icons
-SectionEnd
-
-!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
- !insertmacro MUI_DESCRIPTION_TEXT ${InstallFiles} $(DESC_InstallFiles)
- !insertmacro MUI_DESCRIPTION_TEXT ${StartMenu} $(DESC_StartMenu)
- !insertmacro MUI_DESCRIPTION_TEXT ${DesktopShortcut} $(DESC_DesktopShortcut)
- !insertmacro MUI_DESCRIPTION_TEXT ${BlendRegister} $(DESC_BlendRegister)
-!insertmacro MUI_FUNCTION_DESCRIPTION_END
-
+;
+; $Id$
+;
+; Blender Self-Installer for Windows (NSIS - http://nsis.sourceforge.net)
+;
+
+SetCompressor /SOLID lzma
+
+Name "Blender [VERSION]"
+
+RequestExecutionLevel admin
+
+!include "MUI.nsh"
+!include "WinVer.nsh"
+!include "FileFunc.nsh"
+!include "WordFunc.nsh"
+!include "nsDialogs.nsh"
+!include "x64.nsh"
+
+!define MUI_ABORTWARNING
+
+!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of Blender. It is recommended that you close all other applications before starting Setup."
+!define MUI_WELCOMEFINISHPAGE_BITMAP "[RELDIR]\01.installer.bmp"
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_BITMAP "[RELDIR]\00.header.bmp"
+!define MUI_COMPONENTSPAGE_SMALLDESC
+!define MUI_FINISHPAGE_RUN "$INSTDIR\blender.exe"
+!define MUI_CHECKBITMAP "[RELDIR]\00.checked.bmp"
+!define MUI_UNWELCOMEFINISHPAGE_BITMAP "[RELDIR]\01.installer.bmp"
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_LICENSE "[DISTDIR]\Copyright.txt"
+!insertmacro MUI_PAGE_COMPONENTS
+
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_UNPAGE_WELCOME
+UninstPage custom un.OptionalRemoveConfig un.OptionalRemoveConfigOnLeave
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+!insertmacro Locate
+!insertmacro VersionCompare
+
+
+Icon "[RELDIR]\00.installer.ico"
+UninstallIcon "[RELDIR]\00.installer.ico"
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+ ;Description
+ LangString DESC_InstallFiles ${LANG_ENGLISH} "Copy all required files to the application folder."
+ LangString DESC_StartMenu ${LANG_ENGLISH} "Add shortcut items to the Start Menu. (Recommended)"
+ LangString DESC_DesktopShortcut ${LANG_ENGLISH} "Add a shortcut to Blender on your desktop."
+ LangString DESC_BlendRegister ${LANG_ENGLISH} "Blender can register itself with .blend files to allow double-clicking from Windows Explorer, etc."
+;--------------------------------
+;Data
+
+Caption "Blender [VERSION] Installer"
+OutFile "[DISTDIR]\..\blender-[VERSION]-windows[BITNESS].exe"
+InstallDir $INSTDIR ; $INSTDIR is set inside .onInit
+BrandingText "Blender Foundation | http://www.blender.org"
+ComponentText "This will install Blender [VERSION] on your computer."
+
+DirText "Use the field below to specify the folder where you want Blender to be copied to. To specify a different folder, type a new name or use the Browse button to select an existing folder."
+
+SilentUnInstall normal
+
+Var SHORTVERSION ; This is blender_version_decimal() from path_util.c
+Var BLENDERCONFIG
+Var REMOVECONFIG
+
+; Custom controls
+Var HWND
+
+Var HWND_KEEPCONFIG
+Var HWND_REMOVECONFIG
+
+Function .onInit
+ ClearErrors
+ StrCpy $SHORTVERSION "[SHORTVERSION]"
+
+ ${If} ${RunningX64}
+ ${If} "[BITNESS]" == "32"
+ ${OrIf} "[BITNESS]" == "-mingw"
+ StrCpy $INSTDIR "$PROGRAMFILES32\Blender Foundation\Blender" ; Can't use InstallDir inside Section
+ ${ElseIf} "[BITNESS]" == "64"
+ StrCpy $INSTDIR "$PROGRAMFILES64\Blender Foundation\Blender"
+ ${EndIf}
+ ${Else}
+ StrCpy $INSTDIR "$PROGRAMFILES\Blender Foundation\Blender"
+ ${EndIf}
+FunctionEnd
+
+Function un.onInit
+ SetShellVarContext current
+ StrCpy $BLENDERCONFIG "$APPDATA\Blender Foundation\Blender"
+ SetShellVarContext all
+FunctionEnd
+
+Function un.OptionalRemoveConfig
+ nsDialogs::Create /NOUNLOAD 1018
+ Pop $HWND
+
+ ${If} $HWND == error
+ Abort
+ ${EndIf}
+
+ ${NSD_CreateRadioButton} 0 50 100% 12u "Keep configuration files, autosaved .blend files and installed addons (recommended)"
+ Pop $HWND_KEEPCONFIG
+ ${NSD_CreateRadioButton} 0 80 100% 12u "Remove all files, including configuration files, autosaved .blend files and installed addons"
+ Pop $HWND_REMOVECONFIG
+
+ SendMessage $HWND_KEEPCONFIG ${BM_SETCHECK} 1 0
+
+ nsDialogs::Show
+
+FunctionEnd
+
+Function un.OptionalRemoveConfigOnLeave
+ ${NSD_GetState} $HWND_REMOVECONFIG $R0
+ ${If} $R0 == "1"
+ StrCpy $REMOVECONFIG "1"
+ ${Else}
+ StrCpy $REMOVECONFIG "0"
+ ${EndIf}
+FunctionEnd
+
+
+Section "Blender [VERSION] (required)" InstallFiles
+ SectionIn RO
+
+ ; Set output path to the installation directory.
+ SetOutPath $INSTDIR
+ ; The contents of Blender installation root dir
+ [ROOTDIRCONTS]
+
+ ; All datafiles (python, scripts, datafiles)
+ [DODATAFILES]
+
+ SetOutPath $INSTDIR
+ ${If} ${RunningX64}
+ SetRegView 64
+ ${EndIf}
+ ; Write the installation path into the registry
+ WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "Install_Dir" "$INSTDIR"
+ WriteRegStr HKLM "SOFTWARE\BlenderFoundation" "ShortVersion" "[SHORTVERSION]"
+ ; Write the uninstall keys for Windows
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayName" "Blender"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "Publisher" "Blender Foundation"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "URLInfoAbout" "http://www.blender.org/"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayVersion" "[VERSION]"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "DisplayIcon" "$INSTDIR\blender.exe"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "UninstallString" '"$INSTDIR\uninstall.exe"'
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "NoModify" 1
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Blender" "NoRepair " 1
+ WriteUninstaller "uninstall.exe"
+
+SectionEnd
+
+Section "Add Start Menu Shortcuts" StartMenu
+ SetShellVarContext all
+ CreateDirectory "$SMPROGRAMS\Blender Foundation\Blender\"
+ CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
+ CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Blender.lnk" "$INSTDIR\Blender.exe" "" "$INSTDIR\blender.exe" 0
+ CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Readme.lnk" "$INSTDIR\readme.html" "" "" 0
+ CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\Copyright.lnk" "$INSTDIR\Copyright.txt" "" "$INSTDIR\copyright.txt" 0
+ CreateShortCut "$SMPROGRAMS\Blender Foundation\Blender\GPL-license.lnk" "$INSTDIR\GPL-license.txt" "" "$INSTDIR\GPL-license.txt" 0
+ System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' ; refresh icons
+SectionEnd
+
+Section "Add Desktop Shortcut" DesktopShortcut
+ CreateShortCut "$DESKTOP\Blender.lnk" "$INSTDIR\blender.exe" "" "$INSTDIR\blender.exe" 0
+ System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' ; refresh icons
+SectionEnd
+
+Section "Open .blend files with Blender" BlendRegister
+ ExecWait '"$INSTDIR\blender.exe" -r'
+SectionEnd
+
+UninstallText "This will uninstall Blender [VERSION], and all installed files. Hit 'Uninstall' to continue."
+
+Section "Uninstall"
+ ; Remove registry keys
+ ${If} ${RunningX64}
+ SetRegView 64
+ ${EndIf}
+
+ ReadRegStr $SHORTVERSION HKLM "SOFTWARE\BlenderFoundation" "ShortVersion"
+ DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Blender"
+ DeleteRegKey HKLM "SOFTWARE\BlenderFoundation"
+ DeleteRegKey HKCR ".blend"
+ DeleteRegKey HKCR "blendfile"
+ DeleteRegKey HKCR "CLSID\{D45F043D-F17F-4e8a-8435-70971D9FA46D}"
+ SetShellVarContext all
+
+ ; Remove files
+ [DELROOTDIRCONTS]
+ [DELDATAFILES]
+ [DELDATADIRS]
+
+ Delete "$INSTDIR\uninstall.exe"
+
+ ${If} $REMOVECONFIG == "1"
+ RMDir /r "$BLENDERCONFIG\$SHORTVERSION"
+ ${Endif}
+
+ ; Remove install directory if it's empty
+ RMDir $INSTDIR
+ ; Remove shortcuts
+ Delete "$SMPROGRAMS\Blender Foundation\Blender\*.*"
+ Delete "$DESKTOP\Blender.lnk"
+ ; Remove all link related directories and files
+ RMDir "$SMPROGRAMS\Blender Foundation\Blender"
+ RMDir "$SMPROGRAMS\Blender Foundation"
+
+ System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' ; Refresh icons
+SectionEnd
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${InstallFiles} $(DESC_InstallFiles)
+ !insertmacro MUI_DESCRIPTION_TEXT ${StartMenu} $(DESC_StartMenu)
+ !insertmacro MUI_DESCRIPTION_TEXT ${DesktopShortcut} $(DESC_DesktopShortcut)
+ !insertmacro MUI_DESCRIPTION_TEXT ${BlendRegister} $(DESC_BlendRegister)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+