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:
authorNick Samarin <nicks1987@bigmir.net>2011-05-17 00:30:59 +0400
committerNick Samarin <nicks1987@bigmir.net>2011-05-17 00:30:59 +0400
commita918040902bdeb7c9793168710871e4a3b7777a3 (patch)
tree7380f00bce5448d777d09f4be4d7127e8eecec49 /release/scripts
parentdaeca2f8262884c436c5678225704b594ce5347b (diff)
parent99ee18c684da65ba774175c0b57a086e8222464a (diff)
synched with trunk at revision 36569
Diffstat (limited to 'release/scripts')
-rw-r--r--release/scripts/io/netrender/__init__.py69
-rw-r--r--release/scripts/io/netrender/balancing.py195
-rw-r--r--release/scripts/io/netrender/client.py372
-rw-r--r--release/scripts/io/netrender/master.py1079
-rw-r--r--release/scripts/io/netrender/master_html.py315
-rw-r--r--release/scripts/io/netrender/model.py360
-rw-r--r--release/scripts/io/netrender/netrender.css88
-rw-r--r--release/scripts/io/netrender/netrender.js146
-rw-r--r--release/scripts/io/netrender/operators.py564
-rw-r--r--release/scripts/io/netrender/repath.py150
-rw-r--r--release/scripts/io/netrender/slave.py349
-rw-r--r--release/scripts/io/netrender/thumbnail.py81
-rw-r--r--release/scripts/io/netrender/ui.py550
-rw-r--r--release/scripts/io/netrender/utils.py313
-rw-r--r--release/scripts/io/netrender/versioning.py72
-rw-r--r--release/scripts/modules/add_object_utils.py14
-rw-r--r--release/scripts/modules/addon_utils.py326
-rw-r--r--release/scripts/modules/animsys_refactor.py22
-rw-r--r--release/scripts/modules/bpy/__init__.py16
-rw-r--r--release/scripts/modules/bpy/path.py3
-rw-r--r--release/scripts/modules/bpy/utils.py299
-rw-r--r--release/scripts/modules/bpy_types.py79
-rw-r--r--release/scripts/modules/bpyml.py12
-rw-r--r--release/scripts/modules/bpyml_ui.py4
-rw-r--r--release/scripts/modules/console_python.py (renamed from release/scripts/op/console_python.py)52
-rw-r--r--release/scripts/modules/console_shell.py (renamed from release/scripts/op/console_shell.py)11
-rw-r--r--release/scripts/modules/image_utils.py2
-rw-r--r--release/scripts/modules/io_utils.py198
-rw-r--r--release/scripts/modules/keyingsets_utils.py24
-rw-r--r--release/scripts/modules/mesh_utils.py69
-rw-r--r--release/scripts/modules/rna_info.py31
-rw-r--r--release/scripts/modules/sys_info.py31
-rw-r--r--release/scripts/op/fcurve_euler_filter.py89
-rw-r--r--release/scripts/op/uv.py381
-rw-r--r--release/scripts/presets/interaction/blender.py5
-rw-r--r--release/scripts/presets/interaction/maya.py10
-rw-r--r--release/scripts/presets/keyconfig/maya.py442
-rw-r--r--release/scripts/startup/bl_operators/__init__.py58
-rw-r--r--release/scripts/startup/bl_operators/add_mesh_torus.py (renamed from release/scripts/op/add_mesh_torus.py)22
-rw-r--r--release/scripts/startup/bl_operators/animsys_update.py (renamed from release/scripts/op/animsys_update.py)21
-rw-r--r--release/scripts/startup/bl_operators/image.py (renamed from release/scripts/op/image.py)45
-rw-r--r--release/scripts/startup/bl_operators/mesh.py (renamed from release/scripts/op/mesh.py)25
-rw-r--r--release/scripts/startup/bl_operators/nla.py (renamed from release/scripts/op/nla.py)19
-rw-r--r--release/scripts/startup/bl_operators/object.py (renamed from release/scripts/op/object.py)25
-rw-r--r--release/scripts/startup/bl_operators/object_align.py (renamed from release/scripts/op/object_align.py)26
-rw-r--r--release/scripts/startup/bl_operators/object_quick_effects.py296
-rw-r--r--release/scripts/startup/bl_operators/object_randomize_transform.py (renamed from release/scripts/op/object_randomize_transform.py)22
-rw-r--r--release/scripts/startup/bl_operators/presets.py (renamed from release/scripts/op/presets.py)32
-rw-r--r--release/scripts/startup/bl_operators/screen_play_rendered_anim.py (renamed from release/scripts/op/screen_play_rendered_anim.py)25
-rw-r--r--release/scripts/startup/bl_operators/sequencer.py (renamed from release/scripts/op/sequencer.py)14
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_follow_active.py (renamed from release/scripts/op/uvcalc_follow_active.py)20
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_lightmap.py582
-rw-r--r--release/scripts/startup/bl_operators/uvcalc_smart_project.py (renamed from release/scripts/op/uvcalc_smart_project.py)119
-rw-r--r--release/scripts/startup/bl_operators/vertexpaint_dirt.py (renamed from release/scripts/op/vertexpaint_dirt.py)36
-rw-r--r--release/scripts/startup/bl_operators/wm.py (renamed from release/scripts/op/wm.py)228
-rw-r--r--release/scripts/startup/bl_ui/__init__.py121
-rw-r--r--release/scripts/startup/bl_ui/properties_animviz.py (renamed from release/scripts/ui/properties_animviz.py)21
-rw-r--r--release/scripts/startup/bl_ui/properties_data_armature.py (renamed from release/scripts/ui/properties_data_armature.py)118
-rw-r--r--release/scripts/startup/bl_ui/properties_data_bone.py (renamed from release/scripts/ui/properties_data_bone.py)13
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py (renamed from release/scripts/ui/properties_data_camera.py)23
-rw-r--r--release/scripts/startup/bl_ui/properties_data_curve.py (renamed from release/scripts/ui/properties_data_curve.py)25
-rw-r--r--release/scripts/startup/bl_ui/properties_data_empty.py (renamed from release/scripts/ui/properties_data_empty.py)11
-rw-r--r--release/scripts/startup/bl_ui/properties_data_lamp.py (renamed from release/scripts/ui/properties_data_lamp.py)41
-rw-r--r--release/scripts/startup/bl_ui/properties_data_lattice.py (renamed from release/scripts/ui/properties_data_lattice.py)38
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py (renamed from release/scripts/ui/properties_data_mesh.py)23
-rw-r--r--release/scripts/startup/bl_ui/properties_data_metaball.py (renamed from release/scripts/ui/properties_data_metaball.py)12
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py (renamed from release/scripts/ui/properties_data_modifier.py)158
-rw-r--r--release/scripts/startup/bl_ui/properties_game.py (renamed from release/scripts/ui/properties_game.py)53
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py (renamed from release/scripts/ui/properties_material.py)317
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py (renamed from release/scripts/ui/properties_object.py)42
-rw-r--r--release/scripts/startup/bl_ui/properties_object_constraint.py (renamed from release/scripts/ui/properties_object_constraint.py)151
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py (renamed from release/scripts/ui/properties_particle.py)567
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_cloth.py (renamed from release/scripts/ui/properties_physics_cloth.py)20
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py (renamed from release/scripts/ui/properties_physics_common.py)44
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_field.py (renamed from release/scripts/ui/properties_physics_field.py)20
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py (renamed from release/scripts/ui/properties_physics_fluid.py)18
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py (renamed from release/scripts/ui/properties_physics_smoke.py)19
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_softbody.py (renamed from release/scripts/ui/properties_physics_softbody.py)16
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py (renamed from release/scripts/ui/properties_render.py)118
-rw-r--r--release/scripts/startup/bl_ui/properties_scene.py (renamed from release/scripts/ui/properties_scene.py)165
-rw-r--r--release/scripts/startup/bl_ui/properties_texture.py (renamed from release/scripts/ui/properties_texture.py)206
-rw-r--r--release/scripts/startup/bl_ui/properties_world.py (renamed from release/scripts/ui/properties_world.py)10
-rw-r--r--release/scripts/startup/bl_ui/space_console.py (renamed from release/scripts/ui/space_console.py)14
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py (renamed from release/scripts/ui/space_dopesheet.py)104
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py (renamed from release/scripts/ui/space_filebrowser.py)12
-rw-r--r--release/scripts/startup/bl_ui/space_graph.py (renamed from release/scripts/ui/space_graph.py)18
-rw-r--r--release/scripts/startup/bl_ui/space_image.py (renamed from release/scripts/ui/space_image.py)84
-rw-r--r--release/scripts/startup/bl_ui/space_info.py (renamed from release/scripts/ui/space_info.py)29
-rw-r--r--release/scripts/startup/bl_ui/space_logic.py (renamed from release/scripts/ui/space_logic.py)12
-rw-r--r--release/scripts/startup/bl_ui/space_nla.py (renamed from release/scripts/ui/space_nla.py)18
-rw-r--r--release/scripts/startup/bl_ui/space_node.py (renamed from release/scripts/ui/space_node.py)13
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py (renamed from release/scripts/ui/space_outliner.py)12
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py (renamed from release/scripts/ui/space_sequencer.py)57
-rw-r--r--release/scripts/startup/bl_ui/space_text.py (renamed from release/scripts/ui/space_text.py)16
-rw-r--r--release/scripts/startup/bl_ui/space_time.py (renamed from release/scripts/ui/space_time.py)11
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py (renamed from release/scripts/ui/space_userpref.py)266
-rw-r--r--release/scripts/startup/bl_ui/space_userpref_keymap.py (renamed from release/scripts/ui/space_userpref_keymap.py)69
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py (renamed from release/scripts/ui/space_view3d.py)129
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py (renamed from release/scripts/ui/space_view3d_toolbar.py)182
-rw-r--r--release/scripts/startup/keyingsets_builtins.py (renamed from release/scripts/keyingsets/keyingsets_builtins.py)186
-rw-r--r--release/scripts/templates/addon_add_object.py8
-rw-r--r--release/scripts/templates/background_job.py64
-rw-r--r--release/scripts/templates/builtin_keyingset.py3
-rw-r--r--release/scripts/templates/operator_export.py9
-rw-r--r--release/scripts/templates/operator_mesh_add.py2
-rw-r--r--release/scripts/templates/operator_modal.py2
-rw-r--r--release/scripts/templates/operator_modal_timer.py45
-rw-r--r--release/scripts/templates/panel_simple.py2
108 files changed, 4299 insertions, 7876 deletions
diff --git a/release/scripts/io/netrender/__init__.py b/release/scripts/io/netrender/__init__.py
deleted file mode 100644
index 6c4ed4119fc..00000000000
--- a/release/scripts/io/netrender/__init__.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 #####
-
-# This directory is a Python package.
-
-# To support reload properly, try to access a package var, if it's there, reload everything
-if "init_data" in locals():
- import imp
- imp.reload(model)
- imp.reload(operators)
- imp.reload(client)
- imp.reload(slave)
- imp.reload(master)
- imp.reload(master_html)
- imp.reload(utils)
- imp.reload(balancing)
- imp.reload(ui)
- imp.reload(repath)
- imp.reload(versioning)
-else:
- from netrender import model
- from netrender import operators
- from netrender import client
- from netrender import slave
- from netrender import master
- from netrender import master_html
- from netrender import utils
- from netrender import balancing
- from netrender import ui
- from netrender import repath
- from netrender import versioning
-
-jobs = []
-slaves = []
-blacklist = []
-
-init_file = ""
-valid_address = False
-init_data = True
-
-
-def register():
- ui.addProperties()
-
- import bpy
- scene = bpy.context.scene
- if scene:
- netsettings = scene.network_render
- ui.init_data(netsettings)
-
-
-def unregister():
- import bpy
- del bpy.types.Scene.network_render
diff --git a/release/scripts/io/netrender/balancing.py b/release/scripts/io/netrender/balancing.py
deleted file mode 100644
index dde3ad53084..00000000000
--- a/release/scripts/io/netrender/balancing.py
+++ /dev/null
@@ -1,195 +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 #####
-
-import time
-
-from netrender.utils import *
-import netrender.model
-
-class RatingRule:
- def __init__(self):
- self.enabled = True
-
- def id(self):
- return str(id(self))
-
- def rate(self, job):
- return 0
-
-class ExclusionRule:
- def __init__(self):
- self.enabled = True
-
- def id(self):
- return str(id(self))
-
- def test(self, job):
- return False
-
-class PriorityRule:
- def __init__(self):
- self.enabled = True
-
- def id(self):
- return str(id(self))
-
- def test(self, job):
- return False
-
-class Balancer:
- def __init__(self):
- self.rules = []
- self.priorities = []
- self.exceptions = []
-
- def ruleByID(self, rule_id):
- for rule in self.rules:
- if rule.id() == rule_id:
- return rule
- for rule in self.priorities:
- if rule.id() == rule_id:
- return rule
- for rule in self.exceptions:
- if rule.id() == rule_id:
- return rule
-
- return None
-
- def addRule(self, rule):
- self.rules.append(rule)
-
- def addPriority(self, priority):
- self.priorities.append(priority)
-
- def addException(self, exception):
- self.exceptions.append(exception)
-
- def applyRules(self, job):
- return sum((rule.rate(job) for rule in self.rules if rule.enabled))
-
- def applyPriorities(self, job):
- for priority in self.priorities:
- if priority.enabled and priority.test(job):
- return True # priorities are first
-
- return False
-
- def applyExceptions(self, job):
- for exception in self.exceptions:
- if exception.enabled and exception.test(job):
- return True # exceptions are last
-
- return False
-
- def sortKey(self, job):
- return (1 if self.applyExceptions(job) else 0, # exceptions after
- 0 if self.applyPriorities(job) else 1, # priorities first
- self.applyRules(job))
-
- def balance(self, jobs):
- if jobs:
- # use inline copy to make sure the list is still accessible while sorting
- jobs[:] = sorted(jobs, key=self.sortKey)
- return jobs[0]
- else:
- return None
-
-# ==========================
-
-class RatingUsage(RatingRule):
- def __str__(self):
- return "Usage per job"
-
- def rate(self, job):
- # less usage is better
- return job.usage / job.priority
-
-class RatingUsageByCategory(RatingRule):
- def __init__(self, get_jobs):
- super().__init__()
- self.getJobs = get_jobs
-
- def __str__(self):
- return "Usage per category"
-
- def rate(self, job):
- total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
- maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
-
- # less usage is better
- return total_category_usage / maximum_priority
-
-class NewJobPriority(PriorityRule):
- def __init__(self, limit = 1):
- super().__init__()
- self.limit = limit
-
- def setLimit(self, value):
- self.limit = int(value)
-
- def str_limit(self):
- return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
-
- def __str__(self):
- return "Priority to new jobs"
-
- def test(self, job):
- return job.countFrames(status = DONE) < self.limit
-
-class MinimumTimeBetweenDispatchPriority(PriorityRule):
- def __init__(self, limit = 10):
- super().__init__()
- self.limit = limit
-
- def setLimit(self, value):
- self.limit = int(value)
-
- def str_limit(self):
- return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
-
- def __str__(self):
- return "Priority to jobs that haven't been dispatched recently"
-
- def test(self, job):
- return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
-
-class ExcludeQueuedEmptyJob(ExclusionRule):
- def __str__(self):
- return "Exclude non queued or empty jobs"
-
- def test(self, job):
- return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
-
-class ExcludeSlavesLimit(ExclusionRule):
- def __init__(self, count_jobs, count_slaves, limit = 0.75):
- super().__init__()
- self.count_jobs = count_jobs
- self.count_slaves = count_slaves
- self.limit = limit
-
- def setLimit(self, value):
- self.limit = float(value)
-
- def str_limit(self):
- return "more than %.0f%% of all slaves" % (self.limit * 100)
-
- def __str__(self):
- return "Exclude jobs that would use too many slaves"
-
- def test(self, job):
- return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
diff --git a/release/scripts/io/netrender/client.py b/release/scripts/io/netrender/client.py
deleted file mode 100644
index bc43b8cfbb7..00000000000
--- a/release/scripts/io/netrender/client.py
+++ /dev/null
@@ -1,372 +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 #####
-
-import bpy
-import sys, os, re
-import http, http.client, http.server, urllib
-import subprocess, shutil, time, hashlib
-import json
-
-import netrender
-import netrender.model
-import netrender.slave as slave
-import netrender.master as master
-from netrender.utils import *
-
-def addFluidFiles(job, path):
- if os.path.exists(path):
- pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
-
- for fluid_file in sorted(os.listdir(path)):
- match = pattern.match(fluid_file)
-
- if match:
- # fluid frames starts at 0, which explains the +1
- # This is stupid
- current_frame = int(match.groups()[1]) + 1
- job.addFile(path + fluid_file, current_frame, current_frame)
-
-def addPointCache(job, ob, point_cache, default_path):
- if not point_cache.use_disk_cache:
- return
-
-
- name = point_cache.name
- if name == "":
- name = "".join(["%02X" % ord(c) for c in ob.name])
-
- cache_path = bpy.path.abspath(point_cache.filepath) if point_cache.use_external else default_path
-
- index = "%02i" % point_cache.index
-
- if os.path.exists(cache_path):
- pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
-
- cache_files = []
-
- for cache_file in sorted(os.listdir(cache_path)):
- match = pattern.match(cache_file)
-
- if match:
- cache_frame = int(match.groups()[0])
- cache_files.append((cache_frame, cache_file))
-
- cache_files.sort()
-
- if len(cache_files) == 1:
- cache_frame, cache_file = cache_files[0]
- job.addFile(cache_path + cache_file, cache_frame, cache_frame)
- else:
- for i in range(len(cache_files)):
- current_item = cache_files[i]
- next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
- previous_item = cache_files[i - 1] if i > 0 else None
-
- current_frame, current_file = current_item
-
- if not next_item and not previous_item:
- job.addFile(cache_path + current_file, current_frame, current_frame)
- elif next_item and not previous_item:
- next_frame = next_item[0]
- job.addFile(cache_path + current_file, current_frame, next_frame - 1)
- elif not next_item and previous_item:
- previous_frame = previous_item[0]
- job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
- else:
- next_frame = next_item[0]
- previous_frame = previous_item[0]
- job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
-
-def fillCommonJobSettings(job, job_name, netsettings):
- job.name = job_name
- job.category = netsettings.job_category
-
- for slave in netrender.blacklist:
- job.blacklist.append(slave.id)
-
- job.chunks = netsettings.chunks
- job.priority = netsettings.priority
-
- if netsettings.job_type == "JOB_BLENDER":
- job.type = netrender.model.JOB_BLENDER
- elif netsettings.job_type == "JOB_PROCESS":
- job.type = netrender.model.JOB_PROCESS
- elif netsettings.job_type == "JOB_VCS":
- job.type = netrender.model.JOB_VCS
-
-def clientSendJob(conn, scene, anim = False):
- netsettings = scene.network_render
- if netsettings.job_type == "JOB_BLENDER":
- return clientSendJobBlender(conn, scene, anim)
- elif netsettings.job_type == "JOB_VCS":
- return clientSendJobVCS(conn, scene, anim)
-
-def clientSendJobVCS(conn, scene, anim = False):
- netsettings = scene.network_render
- job = netrender.model.RenderJob()
-
- if anim:
- for f in range(scene.frame_start, scene.frame_end + 1):
- job.addFrame(f)
- else:
- job.addFrame(scene.frame_current)
-
- filename = bpy.data.filepath
-
- if not filename.startswith(netsettings.vcs_wpath):
- # this is an error, need better way to handle this
- return
-
- filename = filename[len(netsettings.vcs_wpath):]
-
- if filename[0] in (os.sep, os.altsep):
- filename = filename[1:]
-
- print("CREATING VCS JOB", filename)
-
- job.addFile(filename, signed=False)
-
- job_name = netsettings.job_name
- path, name = os.path.split(filename)
- if job_name == "[default]":
- job_name = name
-
-
- fillCommonJobSettings(job, job_name, netsettings)
-
- # VCS Specific code
- job.version_info = netrender.model.VersioningInfo()
- job.version_info.system = netsettings.vcs_system
- job.version_info.wpath = netsettings.vcs_wpath
- job.version_info.rpath = netsettings.vcs_rpath
- job.version_info.revision = netsettings.vcs_revision
-
- # try to send path first
- conn.request("POST", "/job", json.dumps(job.serialize()))
- response = conn.getresponse()
- response.read()
-
- job_id = response.getheader("job-id")
-
- # a VCS job is always good right now, need error handling
-
- return job_id
-
-def clientSendJobBlender(conn, scene, anim = False):
- netsettings = scene.network_render
- job = netrender.model.RenderJob()
-
- if anim:
- for f in range(scene.frame_start, scene.frame_end + 1):
- job.addFrame(f)
- else:
- job.addFrame(scene.frame_current)
-
- filename = bpy.data.filepath
- job.addFile(filename)
-
- job_name = netsettings.job_name
- path, name = os.path.split(filename)
- if job_name == "[default]":
- job_name = name
-
- ###########################
- # LIBRARIES
- ###########################
- for lib in bpy.data.libraries:
- file_path = bpy.path.abspath(lib.filepath)
- if os.path.exists(file_path):
- job.addFile(file_path)
-
- ###########################
- # IMAGES
- ###########################
- for image in bpy.data.images:
- if image.source == "FILE" and not image.packed_file:
- file_path = bpy.path.abspath(image.filepath)
- if os.path.exists(file_path):
- job.addFile(file_path)
-
- tex_path = os.path.splitext(file_path)[0] + ".tex"
- if os.path.exists(tex_path):
- job.addFile(tex_path)
-
- ###########################
- # FLUID + POINT CACHE
- ###########################
- root, ext = os.path.splitext(name)
- default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
-
- for object in bpy.data.objects:
- for modifier in object.modifiers:
- if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
- addFluidFiles(job, bpy.path.abspath(modifier.settings.filepath))
- elif modifier.type == "CLOTH":
- addPointCache(job, object, modifier.point_cache, default_path)
- elif modifier.type == "SOFT_BODY":
- addPointCache(job, object, modifier.point_cache, default_path)
- elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
- addPointCache(job, object, modifier.domain_settings.point_cache, default_path)
- elif modifier.type == "MULTIRES" and modifier.is_external:
- file_path = bpy.path.abspath(modifier.filepath)
- job.addFile(file_path)
-
- # particles modifier are stupid and don't contain data
- # we have to go through the object property
- for psys in object.particle_systems:
- addPointCache(job, object, psys.point_cache, default_path)
-
- #print(job.files)
-
- fillCommonJobSettings(job, job_name, netsettings)
-
- # try to send path first
- conn.request("POST", "/job", json.dumps(job.serialize()))
- response = conn.getresponse()
- response.read()
-
- job_id = response.getheader("job-id")
-
- # if not ACCEPTED (but not processed), send files
- if response.status == http.client.ACCEPTED:
- for rfile in job.files:
- f = open(rfile.filepath, "rb")
- conn.request("PUT", fileURL(job_id, rfile.index), f)
- f.close()
- response = conn.getresponse()
- response.read()
-
- # server will reply with ACCEPTED until all files are found
-
- return job_id
-
-def requestResult(conn, job_id, frame):
- conn.request("GET", renderURL(job_id, frame))
-
-class NetworkRenderEngine(bpy.types.RenderEngine):
- bl_idname = 'NET_RENDER'
- bl_label = "Network Render"
- bl_use_postprocess = False
- def render(self, scene):
- if scene.network_render.mode == "RENDER_CLIENT":
- self.render_client(scene)
- elif scene.network_render.mode == "RENDER_SLAVE":
- self.render_slave(scene)
- elif scene.network_render.mode == "RENDER_MASTER":
- self.render_master(scene)
- else:
- print("UNKNOWN OPERATION MODE")
-
- def render_master(self, scene):
- netsettings = scene.network_render
-
- address = "" if netsettings.server_address == "[default]" else netsettings.server_address
-
- master.runMaster((address, netsettings.server_port), netsettings.use_master_broadcast, netsettings.use_master_clear, bpy.path.abspath(netsettings.path), self.update_stats, self.test_break)
-
-
- def render_slave(self, scene):
- slave.render_slave(self, scene.network_render, scene.render.threads)
-
- def render_client(self, scene):
- netsettings = scene.network_render
- self.update_stats("", "Network render client initiation")
-
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port)
-
- if conn:
- # Sending file
-
- self.update_stats("", "Network render exporting")
-
- new_job = False
-
- job_id = netsettings.job_id
-
- # reading back result
-
- self.update_stats("", "Network render waiting for results")
-
-
- requestResult(conn, job_id, scene.frame_current)
- response = conn.getresponse()
- buf = response.read()
-
- if response.status == http.client.NO_CONTENT:
- new_job = True
- netsettings.job_id = clientSendJob(conn, scene)
- job_id = netsettings.job_id
-
- requestResult(conn, job_id, scene.frame_current)
- response = conn.getresponse()
- buf = response.read()
-
- while response.status == http.client.ACCEPTED and not self.test_break():
- time.sleep(1)
- requestResult(conn, job_id, scene.frame_current)
- response = conn.getresponse()
- buf = response.read()
-
- # cancel new jobs (animate on network) on break
- if self.test_break() and new_job:
- conn.request("POST", cancelURL(job_id))
- response = conn.getresponse()
- response.read()
- print( response.status, response.reason )
- netsettings.job_id = 0
-
- if response.status != http.client.OK:
- conn.close()
- return
-
- r = scene.render
- x= int(r.resolution_x*r.resolution_percentage*0.01)
- y= int(r.resolution_y*r.resolution_percentage*0.01)
-
- result_path = os.path.join(bpy.path.abspath(netsettings.path), "output.exr")
-
- folder = os.path.split(result_path)[0]
-
- if not os.path.exists(folder):
- os.mkdir(folder)
-
- f = open(result_path, "wb")
-
- f.write(buf)
-
- f.close()
-
- result = self.begin_result(0, 0, x, y)
- result.load_from_file(result_path)
- self.end_result(result)
-
- conn.close()
-
-def compatible(module):
- module = __import__(module)
- for subclass in module.__dict__.values():
- try: subclass.COMPAT_ENGINES.add('NET_RENDER')
- except: pass
- del module
-
-compatible("properties_world")
-compatible("properties_material")
-compatible("properties_data_mesh")
-compatible("properties_data_camera")
-compatible("properties_texture")
diff --git a/release/scripts/io/netrender/master.py b/release/scripts/io/netrender/master.py
deleted file mode 100644
index 793e3bb51bf..00000000000
--- a/release/scripts/io/netrender/master.py
+++ /dev/null
@@ -1,1079 +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 #####
-
-import sys, os
-import http, http.client, http.server, urllib, socket, socketserver, threading
-import subprocess, shutil, time, hashlib
-import pickle
-import select # for select.error
-import json
-
-from netrender.utils import *
-import netrender.model
-import netrender.balancing
-import netrender.master_html
-import netrender.thumbnail as thumbnail
-
-class MRenderFile(netrender.model.RenderFile):
- def __init__(self, filepath, index, start, end, signature):
- super().__init__(filepath, index, start, end, signature)
- self.found = False
-
- def test(self):
- self.found = os.path.exists(self.filepath)
- if self.found and self.signature != None:
- found_signature = hashFile(self.filepath)
- self.found = self.signature == found_signature
-
- return self.found
-
-
-class MRenderSlave(netrender.model.RenderSlave):
- def __init__(self, name, address, stats):
- super().__init__()
- self.id = hashlib.md5(bytes(repr(name) + repr(address), encoding='utf8')).hexdigest()
- self.name = name
- self.address = address
- self.stats = stats
- self.last_seen = time.time()
-
- self.job = None
- self.job_frames = []
-
- netrender.model.RenderSlave._slave_map[self.id] = self
-
- def seen(self):
- self.last_seen = time.time()
-
- def finishedFrame(self, frame_number):
- self.job_frames.remove(frame_number)
- if not self.job_frames:
- self.job = None
-
-class MRenderJob(netrender.model.RenderJob):
- def __init__(self, job_id, job_info):
- super().__init__(job_info)
- self.id = job_id
- self.last_dispatched = time.time()
-
- # force one chunk for process jobs
- if self.type == netrender.model.JOB_PROCESS:
- self.chunks = 1
-
- # Force WAITING status on creation
- self.status = JOB_WAITING
-
- # special server properties
- self.last_update = 0
- self.save_path = ""
- self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end, rfile.signature) for rfile in job_info.files]
-
- def initInfo(self):
- if not self.resolution:
- self.resolution = tuple(getFileInfo(self.files[0].filepath, ["bpy.context.scene.render.resolution_x", "bpy.context.scene.render.resolution_y", "bpy.context.scene.render.resolution_percentage"]))
-
- def save(self):
- if self.save_path:
- f = open(os.path.join(self.save_path, "job.txt"), "w")
- f.write(json.dumps(self.serialize()))
- f.close()
-
- def edit(self, info_map):
- if "status" in info_map:
- self.status = info_map["status"]
-
- if "priority" in info_map:
- self.priority = info_map["priority"]
-
- if "chunks" in info_map:
- self.chunks = info_map["chunks"]
-
- def testStart(self):
- # Don't test files for versionned jobs
- if not self.version_info:
- for f in self.files:
- if not f.test():
- return False
-
- self.start()
- self.initInfo()
- return True
-
- def testFinished(self):
- for f in self.frames:
- if f.status == QUEUED or f.status == DISPATCHED:
- break
- else:
- self.status = JOB_FINISHED
-
- def pause(self, status = None):
- if self.status not in {JOB_PAUSED, JOB_QUEUED}:
- return
-
- if status is None:
- self.status = JOB_PAUSED if self.status == JOB_QUEUED else JOB_QUEUED
- elif status:
- self.status = JOB_QUEUED
- else:
- self.status = JOB_PAUSED
-
- def start(self):
- self.status = JOB_QUEUED
-
- def addLog(self, frames):
- log_name = "_".join(("%06d" % f for f in frames)) + ".log"
- log_path = os.path.join(self.save_path, log_name)
-
- for number in frames:
- frame = self[number]
- if frame:
- frame.log_path = log_path
-
- def addFrame(self, frame_number, command):
- frame = MRenderFrame(frame_number, command)
- self.frames.append(frame)
- return frame
-
- def reset(self, all):
- for f in self.frames:
- f.reset(all)
-
- def getFrames(self):
- frames = []
- for f in self.frames:
- if f.status == QUEUED:
- self.last_dispatched = time.time()
- frames.append(f)
- if len(frames) >= self.chunks:
- break
-
- return frames
-
-class MRenderFrame(netrender.model.RenderFrame):
- def __init__(self, frame, command):
- super().__init__()
- self.number = frame
- self.slave = None
- self.time = 0
- self.status = QUEUED
- self.command = command
-
- self.log_path = None
-
- def reset(self, all):
- if all or self.status == ERROR:
- self.log_path = None
- self.slave = None
- self.time = 0
- self.status = QUEUED
-
-
-# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-file_pattern = re.compile("/file_([a-zA-Z0-9]+)_([0-9]+)")
-render_pattern = re.compile("/render_([a-zA-Z0-9]+)_([0-9]+).exr")
-thumb_pattern = re.compile("/thumb_([a-zA-Z0-9]+)_([0-9]+).jpg")
-log_pattern = re.compile("/log_([a-zA-Z0-9]+)_([0-9]+).log")
-reset_pattern = re.compile("/reset(all|)_([a-zA-Z0-9]+)_([0-9]+)")
-cancel_pattern = re.compile("/cancel_([a-zA-Z0-9]+)")
-pause_pattern = re.compile("/pause_([a-zA-Z0-9]+)")
-edit_pattern = re.compile("/edit_([a-zA-Z0-9]+)")
-
-class RenderHandler(http.server.BaseHTTPRequestHandler):
- def log_message(self, format, *args):
- # override because the original calls self.address_string(), which
- # is extremely slow due to some timeout..
- sys.stderr.write("[%s] %s\n" % (self.log_date_time_string(), format%args))
-
- def getInfoMap(self):
- length = int(self.headers['content-length'])
-
- if length > 0:
- msg = str(self.rfile.read(length), encoding='utf8')
- return json.loads(msg)
- else:
- return {}
-
- def send_head(self, code = http.client.OK, headers = {}, content = "application/octet-stream"):
- self.send_response(code)
- self.send_header("Content-type", content)
-
- for key, value in headers.items():
- self.send_header(key, value)
-
- self.end_headers()
-
- def do_HEAD(self):
-
- if self.path == "/status":
- job_id = self.headers.get('job-id', "")
- job_frame = int(self.headers.get('job-frame', -1))
-
- job = self.server.getJobID(job_id)
- if job:
- frame = job[job_frame]
-
-
- if frame:
- self.send_head(http.client.OK)
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-
- def do_GET(self):
-
- if self.path == "/version":
- self.send_head()
- self.server.stats("", "Version check")
- self.wfile.write(VERSION)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/render"):
- match = render_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
- frame_number = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- frame = job[frame_number]
-
- if frame:
- if frame.status in (QUEUED, DISPATCHED):
- self.send_head(http.client.ACCEPTED)
- elif frame.status == DONE:
- self.server.stats("", "Sending result to client")
-
- filename = os.path.join(job.save_path, "%06d.exr" % frame_number)
-
- f = open(filename, 'rb')
- self.send_head(content = "image/x-exr")
- shutil.copyfileobj(f, self.wfile)
- f.close()
- elif frame.status == ERROR:
- self.send_head(http.client.PARTIAL_CONTENT)
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else:
- # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/thumb"):
- match = thumb_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
- frame_number = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- frame = job[frame_number]
-
- if frame:
- if frame.status in (QUEUED, DISPATCHED):
- self.send_head(http.client.ACCEPTED)
- elif frame.status == DONE:
- filename = os.path.join(job.save_path, "%06d.exr" % frame_number)
-
- thumbname = thumbnail.generate(filename)
-
- if thumbname:
- f = open(thumbname, 'rb')
- self.send_head(content = "image/jpeg")
- shutil.copyfileobj(f, self.wfile)
- f.close()
- else: # thumbnail couldn't be generated
- self.send_head(http.client.PARTIAL_CONTENT)
- return
- elif frame.status == ERROR:
- self.send_head(http.client.PARTIAL_CONTENT)
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else:
- # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/log"):
- match = log_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
- frame_number = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- frame = job[frame_number]
-
- if frame:
- if not frame.log_path or frame.status in (QUEUED, DISPATCHED):
- self.send_head(http.client.PROCESSING)
- else:
- self.server.stats("", "Sending log to client")
- f = open(frame.log_path, 'rb')
-
- self.send_head(content = "text/plain")
-
- shutil.copyfileobj(f, self.wfile)
-
- f.close()
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else:
- # invalid URL
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/status":
- job_id = self.headers.get('job-id', "")
- job_frame = int(self.headers.get('job-frame', -1))
-
- if job_id:
-
- job = self.server.getJobID(job_id)
- if job:
- if job_frame != -1:
- frame = job[frame]
-
- if frame:
- message = frame.serialize()
- else:
- # no such frame
- self.send_heat(http.client.NO_CONTENT)
- return
- else:
- message = job.serialize()
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- return
- else: # status of all jobs
- message = []
-
- for job in self.server:
- message.append(job.serialize())
-
-
- self.server.stats("", "Sending status")
- self.send_head()
- self.wfile.write(bytes(json.dumps(message), encoding='utf8'))
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/job":
- self.server.balance()
-
- slave_id = self.headers['slave-id']
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job, frames = self.server.newDispatch(slave_id)
-
- if job and frames:
- for f in frames:
- print("dispatch", f.number)
- f.status = DISPATCHED
- f.slave = slave
-
- slave.job = job
- slave.job_frames = [f.number for f in frames]
-
- self.send_head(headers={"job-id": job.id})
-
- message = job.serialize(frames)
-
- self.wfile.write(bytes(json.dumps(message), encoding='utf8'))
-
- self.server.stats("", "Sending job to slave")
- else:
- # no job available, return error code
- slave.job = None
- slave.job_frames = []
-
- self.send_head(http.client.ACCEPTED)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/file"):
- match = file_pattern.match(self.path)
-
- if match:
- slave_id = self.headers['slave-id']
- slave = self.server.getSeenSlave(slave_id)
-
- if not slave:
- # invalid slave id
- print("invalid slave id")
-
- job_id = match.groups()[0]
- file_index = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
- render_file = job.files[file_index]
-
- if render_file:
- self.server.stats("", "Sending file to slave")
- f = open(render_file.filepath, 'rb')
-
- self.send_head()
- shutil.copyfileobj(f, self.wfile)
-
- f.close()
- else:
- # no such file
- self.send_head(http.client.NO_CONTENT)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/slaves":
- message = []
-
- self.server.stats("", "Sending slaves status")
-
- for slave in self.server.slaves:
- message.append(slave.serialize())
-
- self.send_head()
-
- self.wfile.write(bytes(json.dumps(message), encoding='utf8'))
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- else:
- # hand over the rest to the html section
- netrender.master_html.get(self)
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- def do_POST(self):
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- if self.path == "/job":
-
- length = int(self.headers['content-length'])
-
- job_info = netrender.model.RenderJob.materialize(json.loads(str(self.rfile.read(length), encoding='utf8')))
-
- job_id = self.server.nextJobID()
-
- job = MRenderJob(job_id, job_info)
-
- for frame in job_info.frames:
- frame = job.addFrame(frame.number, frame.command)
-
- self.server.addJob(job)
-
- headers={"job-id": job_id}
-
- if job.testStart():
- self.server.stats("", "New job, started")
- self.send_head(headers=headers)
- else:
- self.server.stats("", "New job, missing files (%i total)" % len(job.files))
- self.send_head(http.client.ACCEPTED, headers=headers)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/edit"):
- match = edit_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- info_map = self.getInfoMap()
-
- job.edit(info_map)
- self.send_head()
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else:
- # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/balance_limit":
- info_map = self.getInfoMap()
- for rule_id, limit in info_map.items():
- try:
- rule = self.server.balancer.ruleByID(rule_id)
- if rule:
- rule.setLimit(limit)
- except:
- pass # invalid type
-
- self.send_head()
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/balance_enable":
- info_map = self.getInfoMap()
- for rule_id, enabled in info_map.items():
- rule = self.server.balancer.ruleByID(rule_id)
- if rule:
- rule.enabled = enabled
-
- self.send_head()
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/cancel"):
- match = cancel_pattern.match(self.path)
-
- if match:
- info_map = self.getInfoMap()
- clear = info_map.get("clear", False)
-
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- self.server.stats("", "Cancelling job")
- self.server.removeJob(job, clear)
- self.send_head()
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else:
- # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/pause"):
- match = pause_pattern.match(self.path)
-
- if match:
- info_map = self.getInfoMap()
- status = info_map.get("status", None)
-
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- self.server.stats("", "Pausing job")
- job.pause(status)
- self.send_head()
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else:
- # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/clear":
- # cancel all jobs
- info_map = self.getInfoMap()
- clear = info_map.get("clear", False)
-
- self.server.stats("", "Clearing jobs")
- self.server.clear(clear)
-
- self.send_head()
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/reset"):
- match = reset_pattern.match(self.path)
-
- if match:
- all = match.groups()[0] == 'all'
- job_id = match.groups()[1]
- job_frame = int(match.groups()[2])
-
- job = self.server.getJobID(job_id)
-
- if job:
- if job_frame != 0:
-
- frame = job[job_frame]
- if frame:
- self.server.stats("", "Reset job frame")
- frame.reset(all)
- self.send_head()
- else:
- # no such frame
- self.send_head(http.client.NO_CONTENT)
-
- else:
- self.server.stats("", "Reset job")
- job.reset(all)
- self.send_head()
-
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/slave":
- length = int(self.headers['content-length'])
- job_frame_string = self.headers['job-frame']
-
- self.server.stats("", "New slave connected")
-
- slave_info = netrender.model.RenderSlave.materialize(json.loads(str(self.rfile.read(length), encoding='utf8')), cache = False)
-
- slave_id = self.server.addSlave(slave_info.name, self.client_address, slave_info.stats)
-
- self.send_head(headers = {"slave-id": slave_id})
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/log":
- length = int(self.headers['content-length'])
-
- log_info = netrender.model.LogFile.materialize(json.loads(str(self.rfile.read(length), encoding='utf8')))
-
- slave_id = log_info.slave_id
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job = self.server.getJobID(log_info.job_id)
-
- if job:
- self.server.stats("", "Log announcement")
- job.addLog(log_info.frames)
- self.send_head(http.client.OK)
- else:
- # no such job id
- self.send_head(http.client.NO_CONTENT)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- def do_PUT(self):
-
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- if self.path.startswith("/file"):
- match = file_pattern.match(self.path)
-
- if match:
- self.server.stats("", "Receiving job")
-
- length = int(self.headers['content-length'])
- job_id = match.groups()[0]
- file_index = int(match.groups()[1])
-
- job = self.server.getJobID(job_id)
-
- if job:
-
- render_file = job.files[file_index]
-
- if render_file:
- main_file = job.files[0].filepath # filename of the first file
-
- main_path, main_name = os.path.split(main_file)
-
- if file_index > 0:
- file_path = prefixPath(job.save_path, render_file.filepath, main_path)
- else:
- file_path = os.path.join(job.save_path, main_name)
-
- buf = self.rfile.read(length)
-
- # add same temp file + renames as slave
-
- f = open(file_path, "wb")
- f.write(buf)
- f.close()
- del buf
-
- render_file.filepath = file_path # set the new path
-
- if job.testStart():
- self.server.stats("", "File upload, starting job")
- self.send_head(http.client.OK)
- else:
- self.server.stats("", "File upload, file missings")
- self.send_head(http.client.ACCEPTED)
- else: # invalid file
- print("file not found", job_id, file_index)
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- print("job not found", job_id, file_index)
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- print("no match")
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/render":
- self.server.stats("", "Receiving render result")
-
- # need some message content here or the slave doesn't like it
- self.wfile.write(bytes("foo", encoding='utf8'))
-
- slave_id = self.headers['slave-id']
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job_id = self.headers['job-id']
-
- job = self.server.getJobID(job_id)
-
- if job:
- job_frame = int(self.headers['job-frame'])
- job_result = int(self.headers['job-result'])
- job_time = float(self.headers['job-time'])
-
- frame = job[job_frame]
-
- if frame:
- if job.hasRenderResult():
- if job_result == DONE:
- length = int(self.headers['content-length'])
- buf = self.rfile.read(length)
- f = open(os.path.join(job.save_path, "%06d.exr" % job_frame), 'wb')
- f.write(buf)
- f.close()
-
- del buf
- elif job_result == ERROR:
- # blacklist slave on this job on error
- # slaves might already be in blacklist if errors on the whole chunk
- if not slave.id in job.blacklist:
- job.blacklist.append(slave.id)
-
- slave.finishedFrame(job_frame)
-
- frame.status = job_result
- frame.time = job_time
-
- job.testFinished()
-
- self.send_head()
- else: # frame not found
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path == "/thumb":
- self.server.stats("", "Receiving thumbnail result")
-
- # need some message content here or the slave doesn't like it
- self.wfile.write(bytes("foo", encoding='utf8'))
-
- slave_id = self.headers['slave-id']
-
- slave = self.server.getSeenSlave(slave_id)
-
- if slave: # only if slave id is valid
- job_id = self.headers['job-id']
-
- job = self.server.getJobID(job_id)
-
- if job:
- job_frame = int(self.headers['job-frame'])
-
- frame = job[job_frame]
-
- if frame:
- if job.hasRenderResult():
- length = int(self.headers['content-length'])
- buf = self.rfile.read(length)
- f = open(os.path.join(job.save_path, "%06d.jpg" % job_frame), 'wb')
- f.write(buf)
- f.close()
-
- del buf
-
- else: # frame not found
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid slave id
- self.send_head(http.client.NO_CONTENT)
- # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- elif self.path.startswith("/log"):
- self.server.stats("", "Receiving log file")
-
- match = log_pattern.match(self.path)
-
- if match:
- job_id = match.groups()[0]
-
- job = self.server.getJobID(job_id)
-
- if job:
- job_frame = int(match.groups()[1])
-
- frame = job[job_frame]
-
- if frame and frame.log_path:
- length = int(self.headers['content-length'])
- buf = self.rfile.read(length)
- f = open(frame.log_path, 'ab')
- f.write(buf)
- f.close()
-
- del buf
-
- self.server.getSeenSlave(self.headers['slave-id'])
-
- self.send_head()
- else: # frame not found
- self.send_head(http.client.NO_CONTENT)
- else: # job not found
- self.send_head(http.client.NO_CONTENT)
- else: # invalid url
- self.send_head(http.client.NO_CONTENT)
-
-class RenderMasterServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
- def __init__(self, address, handler_class, path, subdir=True):
- super().__init__(address, handler_class)
- self.jobs = []
- self.jobs_map = {}
- self.slaves = []
- self.slaves_map = {}
- self.job_id = 0
-
- if subdir:
- self.path = os.path.join(path, "master_" + str(os.getpid()))
- else:
- self.path = path
-
- self.slave_timeout = 5 # 5 mins: need a parameter for that
-
- self.balancer = netrender.balancing.Balancer()
- self.balancer.addRule(netrender.balancing.RatingUsageByCategory(self.getJobs))
- self.balancer.addRule(netrender.balancing.RatingUsage())
- self.balancer.addException(netrender.balancing.ExcludeQueuedEmptyJob())
- self.balancer.addException(netrender.balancing.ExcludeSlavesLimit(self.countJobs, self.countSlaves, limit = 0.9))
- self.balancer.addPriority(netrender.balancing.NewJobPriority())
- self.balancer.addPriority(netrender.balancing.MinimumTimeBetweenDispatchPriority(limit = 2))
-
- if not os.path.exists(self.path):
- os.mkdir(self.path)
-
- def restore(self, jobs, slaves, balancer = None):
- self.jobs = jobs
- self.jobs_map = {}
-
- for job in self.jobs:
- self.jobs_map[job.id] = job
- self.job_id = max(self.job_id, int(job.id))
-
- self.slaves = slaves
- for slave in self.slaves:
- self.slaves_map[slave.id] = slave
-
- if balancer:
- self.balancer = balancer
-
-
- def nextJobID(self):
- self.job_id += 1
- return str(self.job_id)
-
- def addSlave(self, name, address, stats):
- slave = MRenderSlave(name, address, stats)
- self.slaves.append(slave)
- self.slaves_map[slave.id] = slave
-
- return slave.id
-
- def removeSlave(self, slave):
- self.slaves.remove(slave)
- self.slaves_map.pop(slave.id)
-
- def getSlave(self, slave_id):
- return self.slaves_map.get(slave_id)
-
- def getSeenSlave(self, slave_id):
- slave = self.getSlave(slave_id)
- if slave:
- slave.seen()
-
- return slave
-
- def timeoutSlaves(self):
- removed = []
-
- t = time.time()
-
- for slave in self.slaves:
- if (t - slave.last_seen) / 60 > self.slave_timeout:
- removed.append(slave)
-
- if slave.job:
- for f in slave.job_frames:
- slave.job[f].status = ERROR
-
- for slave in removed:
- self.removeSlave(slave)
-
- def updateUsage(self):
- blend = 0.5
- for job in self.jobs:
- job.usage *= (1 - blend)
-
- if self.slaves:
- slave_usage = blend / self.countSlaves()
-
- for slave in self.slaves:
- if slave.job:
- slave.job.usage += slave_usage
-
-
- def clear(self, clear_files = False):
- removed = self.jobs[:]
-
- for job in removed:
- self.removeJob(job, clear_files)
-
- def balance(self):
- self.balancer.balance(self.jobs)
-
- def getJobs(self):
- return self.jobs
-
- def countJobs(self, status = JOB_QUEUED):
- total = 0
- for j in self.jobs:
- if j.status == status:
- total += 1
-
- return total
-
- def countSlaves(self):
- return len(self.slaves)
-
- def removeJob(self, job, clear_files = False):
- self.jobs.remove(job)
- self.jobs_map.pop(job.id)
-
- if clear_files:
- shutil.rmtree(job.save_path)
-
- for slave in self.slaves:
- if slave.job == job:
- slave.job = None
- slave.job_frames = []
-
- def addJob(self, job):
- self.jobs.append(job)
- self.jobs_map[job.id] = job
-
- # create job directory
- job.save_path = os.path.join(self.path, "job_" + job.id)
- if not os.path.exists(job.save_path):
- os.mkdir(job.save_path)
-
- job.save()
-
- def getJobID(self, id):
- return self.jobs_map.get(id)
-
- def __iter__(self):
- for job in self.jobs:
- yield job
-
- def newDispatch(self, slave_id):
- if self.jobs:
- for job in self.jobs:
- if not self.balancer.applyExceptions(job) and slave_id not in job.blacklist:
- return job, job.getFrames()
-
- return None, None
-
-def clearMaster(path):
- shutil.rmtree(path)
-
-def createMaster(address, clear, path):
- filepath = os.path.join(path, "blender_master.data")
-
- if not clear and os.path.exists(filepath):
- print("loading saved master:", filepath)
- with open(filepath, 'rb') as f:
- path, jobs, slaves = pickle.load(f)
-
- httpd = RenderMasterServer(address, RenderHandler, path, subdir=False)
- httpd.restore(jobs, slaves)
-
- return httpd
-
- return RenderMasterServer(address, RenderHandler, path)
-
-def saveMaster(path, httpd):
- filepath = os.path.join(path, "blender_master.data")
-
- with open(filepath, 'wb') as f:
- pickle.dump((httpd.path, httpd.jobs, httpd.slaves), f, pickle.HIGHEST_PROTOCOL)
-
-def runMaster(address, broadcast, clear, path, update_stats, test_break):
- httpd = createMaster(address, clear, path)
- httpd.timeout = 1
- httpd.stats = update_stats
-
- if broadcast:
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-
- start_time = time.time() - 2
-
- while not test_break():
- try:
- httpd.handle_request()
- except select.error:
- pass
-
- if time.time() - start_time >= 2: # need constant here
- httpd.timeoutSlaves()
-
- httpd.updateUsage()
-
- if broadcast:
- print("broadcasting address")
- s.sendto(bytes("%i" % address[1], encoding='utf8'), 0, ('<broadcast>', 8000))
- start_time = time.time()
-
- httpd.server_close()
- if clear:
- clearMaster(httpd.path)
- else:
- saveMaster(path, httpd)
-
diff --git a/release/scripts/io/netrender/master_html.py b/release/scripts/io/netrender/master_html.py
deleted file mode 100644
index 877273207a8..00000000000
--- a/release/scripts/io/netrender/master_html.py
+++ /dev/null
@@ -1,315 +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 #####
-
-import os
-import re
-import shutil
-from netrender.utils import *
-import netrender.model
-
-src_folder = os.path.split(__file__)[0]
-
-def get(handler):
- def output(text):
- handler.wfile.write(bytes(text, encoding='utf8'))
-
- def head(title, refresh = False):
- output("<html><head>")
- if refresh:
- output("<meta http-equiv='refresh' content=5>")
- output("<script src='/html/netrender.js' type='text/javascript'></script>")
-# output("<script src='/html/json2.js' type='text/javascript'></script>")
- output("<title>")
- output(title)
- output("</title></head><body>")
- output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
-
-
- def link(text, url, script=""):
- return "<a href='%s' %s>%s</a>" % (url, script, text)
-
- def tag(name, text, attr=""):
- return "<%s %s>%s</%s>" % (name, attr, text, name)
-
- def startTable(border=1, class_style = None, caption = None):
- output("<table border='%i'" % border)
-
- if class_style:
- output(" class='%s'" % class_style)
-
- output(">")
-
- if caption:
- output("<caption>%s</caption>" % caption)
-
- def headerTable(*headers):
- output("<thead><tr>")
-
- for c in headers:
- output("<td>" + c + "</td>")
-
- output("</tr></thead>")
-
- def rowTable(*data, id = None, class_style = None, extra = None):
- output("<tr")
-
- if id:
- output(" id='%s'" % id)
-
- if class_style:
- output(" class='%s'" % class_style)
-
- if extra:
- output(" %s" % extra)
-
- output(">")
-
- for c in data:
- output("<td>" + str(c) + "</td>")
-
- output("</tr>")
-
- def endTable():
- output("</table>")
-
- def checkbox(title, value, script=""):
- return """<input type="checkbox" title="%s" %s %s>""" % (title, "checked" if value else "", ("onclick=\"%s\"" % script) if script else "")
-
- if handler.path == "/html/netrender.js":
- f = open(os.path.join(src_folder, "netrender.js"), 'rb')
-
- handler.send_head(content = "text/javascript")
- shutil.copyfileobj(f, handler.wfile)
-
- f.close()
- elif handler.path == "/html/netrender.css":
- f = open(os.path.join(src_folder, "netrender.css"), 'rb')
-
- handler.send_head(content = "text/css")
- shutil.copyfileobj(f, handler.wfile)
-
- f.close()
- elif handler.path == "/html" or handler.path == "/":
- handler.send_head(content = "text/html")
- head("NetRender", refresh = True)
-
- output("<h2>Jobs</h2>")
-
- startTable()
- headerTable(
- "&nbsp;",
- "id",
- "name",
- "category",
- "type",
- "chunks",
- "priority",
- "usage",
- "wait",
- "status",
- "length",
- "done",
- "dispatched",
- "error",
- "priority",
- "exception"
- )
-
- handler.server.balance()
-
- for job in handler.server.jobs:
- results = job.framesStatus()
- rowTable(
- """<button title="cancel job" onclick="cancel_job('%s');">X</button>""" % job.id +
- """<button title="pause job" onclick="request('/pause_%s', null);">P</button>""" % job.id +
- """<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
- job.id,
- link(job.name, "/html/job" + job.id),
- job.category if job.category else "<i>None</i>",
- netrender.model.JOB_TYPES[job.type],
- str(job.chunks) +
- """<button title="increase chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);">+</button>""" % (job.id, job.chunks + 1) +
- """<button title="decrease chunks size" onclick="request('/edit_%s', &quot;{'chunks': %i}&quot;);" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
- str(job.priority) +
- """<button title="increase priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);">+</button>""" % (job.id, job.priority + 1) +
- """<button title="decrease priority" onclick="request('/edit_%s', &quot;{'priority': %i}&quot;);" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
- "%0.1f%%" % (job.usage * 100),
- "%is" % int(time.time() - job.last_dispatched),
- job.statusText(),
- len(job),
- results[DONE],
- results[DISPATCHED],
- str(results[ERROR]) +
- """<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
- "yes" if handler.server.balancer.applyPriorities(job) else "no",
- "yes" if handler.server.balancer.applyExceptions(job) else "no"
- )
-
- endTable()
-
- output("<h2>Slaves</h2>")
-
- startTable()
- headerTable("name", "address", "last seen", "stats", "job")
-
- for slave in handler.server.slaves:
- rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
-
- endTable()
-
- output("<h2>Configuration</h2>")
-
- output("""<button title="remove all jobs" onclick="clear_jobs();">CLEAR JOB LIST</button>""")
-
- startTable(caption = "Rules", class_style = "rules")
-
- headerTable("type", "enabled", "description", "limit")
-
- for rule in handler.server.balancer.rules:
- rowTable(
- "rating",
- checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())),
- rule,
- rule.str_limit() +
- """<button title="edit limit" onclick="balance_edit('%s', '%s');">edit</button>""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
- )
-
- for rule in handler.server.balancer.priorities:
- rowTable(
- "priority",
- checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())),
- rule,
- rule.str_limit() +
- """<button title="edit limit" onclick="balance_edit('%s', '%s');">edit</button>""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
- )
-
- for rule in handler.server.balancer.exceptions:
- rowTable(
- "exception",
- checkbox("", rule.enabled, "balance_enable('%s', '%s')" % (rule.id(), str(not rule.enabled).lower())),
- rule,
- rule.str_limit() +
- """<button title="edit limit" onclick="balance_edit('%s', '%s');">edit</button>""" % (rule.id(), str(rule.limit)) if hasattr(rule, "limit") else "&nbsp;"
- )
-
- endTable()
-
- output("</body></html>")
-
- elif handler.path.startswith("/html/job"):
- handler.send_head(content = "text/html")
- job_id = handler.path[9:]
-
- head("NetRender")
-
- job = handler.server.getJobID(job_id)
-
- if job:
- output("<h2>Render Information</h2>")
-
- job.initInfo()
-
- startTable()
-
- rowTable("resolution", "%ix%i at %i%%" % job.resolution)
-
- endTable()
-
-
- if job.type == netrender.model.JOB_BLENDER:
- output("<h2>Files</h2>")
-
- startTable()
- headerTable("path")
-
- tot_cache = 0
- tot_fluid = 0
-
- rowTable(job.files[0].filepath)
- rowTable("Other Files", class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.other&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
-
- for file in job.files:
- if file.filepath.endswith(".bphys"):
- tot_cache += 1
- elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
- tot_fluid += 1
- else:
- if file != job.files[0]:
- rowTable(file.filepath, class_style = "other")
-
- if tot_cache > 0:
- rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.cache&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
- for file in job.files:
- if file.filepath.endswith(".bphys"):
- rowTable(os.path.split(file.filepath)[1], class_style = "cache")
-
- if tot_fluid > 0:
- rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(&quot;.fluid&quot;, &quot;none&quot;, &quot;table-row&quot;)'")
- for file in job.files:
- if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
- rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
-
- endTable()
- elif job.type == netrender.model.JOB_VCS:
- output("<h2>Versioning</h2>")
-
- startTable()
-
- rowTable("System", job.version_info.system.name)
- rowTable("Remote Path", job.version_info.rpath)
- rowTable("Working Path", job.version_info.wpath)
- rowTable("Revision", job.version_info.revision)
- rowTable("Render File", job.files[0].filepath)
-
- endTable()
-
- if job.blacklist:
- output("<h2>Blacklist</h2>")
-
- startTable()
- headerTable("name", "address")
-
- for slave_id in job.blacklist:
- slave = handler.server.slaves_map[slave_id]
- rowTable(slave.name, slave.address[0])
-
- endTable()
-
- output("<h2>Frames</h2>")
-
- startTable()
- headerTable("no", "status", "render time", "slave", "log", "result", "")
-
- for frame in job.frames:
- rowTable(
- frame.number,
- frame.statusText(),
- "%.1fs" % frame.time,
- frame.slave.name if frame.slave else "&nbsp;",
- link("view log", logURL(job_id, frame.number)) if frame.log_path else "&nbsp;",
- link("view result", renderURL(job_id, frame.number)) + " [" +
- tag("span", "show", attr="class='thumb' onclick='showThumb(%s, %i)'" % (job.id, frame.number)) + "]" if frame.status == DONE else "&nbsp;",
- "<img name='thumb%i' title='hide thumbnails' src='' class='thumb' onclick='showThumb(%s, %i)'>" % (frame.number, job.id, frame.number)
- )
-
- endTable()
- else:
- output("no such job")
-
- output("</body></html>")
-
diff --git a/release/scripts/io/netrender/model.py b/release/scripts/io/netrender/model.py
deleted file mode 100644
index 5fc0bc2a0bb..00000000000
--- a/release/scripts/io/netrender/model.py
+++ /dev/null
@@ -1,360 +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 #####
-
-import sys, os
-import http, http.client, http.server, urllib
-import subprocess, shutil, time, hashlib
-
-import netrender.versioning as versioning
-from netrender.utils import *
-
-class LogFile:
- def __init__(self, job_id = 0, slave_id = 0, frames = []):
- self.job_id = job_id
- self.slave_id = slave_id
- self.frames = frames
-
- def serialize(self):
- return {
- "job_id": self.job_id,
- "slave_id": self.slave_id,
- "frames": self.frames
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- logfile = LogFile()
- logfile.job_id = data["job_id"]
- logfile.slave_id = data["slave_id"]
- logfile.frames = data["frames"]
-
- return logfile
-
-class RenderSlave:
- _slave_map = {}
-
- def __init__(self):
- self.id = ""
- self.name = ""
- self.address = ("",0)
- self.stats = ""
- self.total_done = 0
- self.total_error = 0
- self.last_seen = 0.0
-
- def serialize(self):
- return {
- "id": self.id,
- "name": self.name,
- "address": self.address,
- "stats": self.stats,
- "total_done": self.total_done,
- "total_error": self.total_error,
- "last_seen": self.last_seen
- }
-
- @staticmethod
- def materialize(data, cache = True):
- if not data:
- return None
-
- slave_id = data["id"]
-
- if cache and slave_id in RenderSlave._slave_map:
- return RenderSlave._slave_map[slave_id]
-
- slave = RenderSlave()
- slave.id = slave_id
- slave.name = data["name"]
- slave.address = data["address"]
- slave.stats = data["stats"]
- slave.total_done = data["total_done"]
- slave.total_error = data["total_error"]
- slave.last_seen = data["last_seen"]
-
- if cache:
- RenderSlave._slave_map[slave_id] = slave
-
- return slave
-
-JOB_BLENDER = 1
-JOB_PROCESS = 2
-JOB_VCS = 3
-
-JOB_TYPES = {
- JOB_BLENDER: "Blender",
- JOB_PROCESS: "Process",
- JOB_VCS: "Versioned",
- }
-
-class VersioningInfo:
- def __init__(self, info = None):
- self._system = None
- self.wpath = ""
- self.rpath = ""
- self.revision = ""
-
- @property
- def system(self):
- return self._system
-
- @system.setter
- def system(self, value):
- self._system = versioning.SYSTEMS[value]
-
- def update(self):
- self.system.update(self)
-
- def serialize(self):
- return {
- "wpath": self.wpath,
- "rpath": self.rpath,
- "revision": self.revision,
- "system": self.system.name
- }
-
- @staticmethod
- def generate(system, path):
- vs = VersioningInfo()
- vs.wpath = path
- vs.system = system
-
- vs.rpath = vs.system.path(path)
- vs.revision = vs.system.revision(path)
-
- return vs
-
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- vs = VersioningInfo()
- vs.wpath = data["wpath"]
- vs.rpath = data["rpath"]
- vs.revision = data["revision"]
- vs.system = data["system"]
-
- return vs
-
-
-class RenderFile:
- def __init__(self, filepath = "", index = 0, start = -1, end = -1, signature=0):
- self.filepath = filepath
- self.original_path = filepath
- self.signature = signature
- self.index = index
- self.start = start
- self.end = end
-
- def serialize(self):
- return {
- "filepath": self.filepath,
- "original_path": self.original_path,
- "index": self.index,
- "start": self.start,
- "end": self.end,
- "signature": self.signature
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"], data["signature"])
- rfile.original_path = data["original_path"]
-
- return rfile
-
-class RenderJob:
- def __init__(self, job_info = None):
- self.id = ""
- self.type = JOB_BLENDER
- self.name = ""
- self.category = "None"
- self.status = JOB_WAITING
- self.files = []
- self.chunks = 0
- self.priority = 0
- self.blacklist = []
-
- self.version_info = None
-
- self.resolution = None
-
- self.usage = 0.0
- self.last_dispatched = 0.0
- self.frames = []
-
- if job_info:
- self.type = job_info.type
- self.name = job_info.name
- self.category = job_info.category
- self.status = job_info.status
- self.files = job_info.files
- self.chunks = job_info.chunks
- self.priority = job_info.priority
- self.blacklist = job_info.blacklist
- self.version_info = job_info.version_info
-
- def hasRenderResult(self):
- return self.type in (JOB_BLENDER, JOB_VCS)
-
- def rendersWithBlender(self):
- return self.type in (JOB_BLENDER, JOB_VCS)
-
- def addFile(self, file_path, start=-1, end=-1, signed=True):
- if signed:
- signature = hashFile(file_path)
- else:
- signature = None
- self.files.append(RenderFile(file_path, len(self.files), start, end, signature))
-
- def addFrame(self, frame_number, command = ""):
- frame = RenderFrame(frame_number, command)
- self.frames.append(frame)
- return frame
-
- def __len__(self):
- return len(self.frames)
-
- def countFrames(self, status=QUEUED):
- total = 0
- for f in self.frames:
- if f.status == status:
- total += 1
-
- return total
-
- def countSlaves(self):
- return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
-
- def statusText(self):
- return JOB_STATUS_TEXT[self.status]
-
- def framesStatus(self):
- results = {
- QUEUED: 0,
- DISPATCHED: 0,
- DONE: 0,
- ERROR: 0
- }
-
- for frame in self.frames:
- results[frame.status] += 1
-
- return results
-
- def __contains__(self, frame_number):
- for f in self.frames:
- if f.number == frame_number:
- return True
- else:
- return False
-
- def __getitem__(self, frame_number):
- for f in self.frames:
- if f.number == frame_number:
- return f
- else:
- return None
-
- def serialize(self, frames = None):
- min_frame = min((f.number for f in frames)) if frames else -1
- max_frame = max((f.number for f in frames)) if frames else -1
- return {
- "id": self.id,
- "type": self.type,
- "name": self.name,
- "category": self.category,
- "status": self.status,
- "files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
- "frames": [f.serialize() for f in self.frames if not frames or f in frames],
- "chunks": self.chunks,
- "priority": self.priority,
- "usage": self.usage,
- "blacklist": self.blacklist,
- "last_dispatched": self.last_dispatched,
- "version_info": self.version_info.serialize() if self.version_info else None,
- "resolution": self.resolution
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- job = RenderJob()
- job.id = data["id"]
- job.type = data["type"]
- job.name = data["name"]
- job.category = data["category"]
- job.status = data["status"]
- job.files = [RenderFile.materialize(f) for f in data["files"]]
- job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
- job.chunks = data["chunks"]
- job.priority = data["priority"]
- job.usage = data["usage"]
- job.blacklist = data["blacklist"]
- job.last_dispatched = data["last_dispatched"]
- job.resolution = data["resolution"]
-
- version_info = data.get("version_info", None)
- if version_info:
- job.version_info = VersioningInfo.materialize(version_info)
-
- return job
-
-class RenderFrame:
- def __init__(self, number = 0, command = ""):
- self.number = number
- self.time = 0
- self.status = QUEUED
- self.slave = None
- self.command = command
-
- def statusText(self):
- return FRAME_STATUS_TEXT[self.status]
-
- def serialize(self):
- return {
- "number": self.number,
- "time": self.time,
- "status": self.status,
- "slave": None if not self.slave else self.slave.serialize(),
- "command": self.command
- }
-
- @staticmethod
- def materialize(data):
- if not data:
- return None
-
- frame = RenderFrame()
- frame.number = data["number"]
- frame.time = data["time"]
- frame.status = data["status"]
- frame.slave = RenderSlave.materialize(data["slave"])
- frame.command = data["command"]
-
- return frame
diff --git a/release/scripts/io/netrender/netrender.css b/release/scripts/io/netrender/netrender.css
deleted file mode 100644
index 0c54690e002..00000000000
--- a/release/scripts/io/netrender/netrender.css
+++ /dev/null
@@ -1,88 +0,0 @@
-body {
- background-color:#eee;
- font-size:12px;
- font-family: "Lucida Sans","Lucida Sans Unicode","Lucida Grande",Lucida,sans-serif;
-
-}
-a {
- /*text-decoration:none;*/
- color:#666;
-}
-a:hover {
- color:#000;
-}
-h2 {
- background-color:#ddd;
- font-size:120%;
- padding:5px;
-}
-
-h2 {
- background-color:#ddd;
- font-size:110%;
- padding:5px;
-}
-
-table {
- text-align:center;
- border:0;
- background-color:#ddd;
- padding: 0px;
- margin: 0px;
-}
-thead{
- font-size:90%;
- color:#555;
- background-color:#ccc;
-}
-td {
- border:0;
- padding:2px;
- padding-left:10px;
- padding-right:10px;
- margin-left:20px;
- background-color:#ddd;
-}
-td:hover {
- background-color:#ccc;
-}
-tr {
- border:0;
-}
-button {
- color: #111;
- width: auto;
- height: auto;
-}
-
-.toggle {
- text-decoration: underline;
- cursor: pointer;
-}
-
-.cache {
- display: none;
-}
-
-.fluid {
- display: none;
-}
-
-.other {
- display: none;
-}
-
-.rules {
- width: 60em;
- text-align: left;
-}
-
-img.thumb {
- display: none;
- cursor: pointer;
-}
-
-span.thumb {
- text-decoration: underline;
- cursor: pointer;
-}
diff --git a/release/scripts/io/netrender/netrender.js b/release/scripts/io/netrender/netrender.js
deleted file mode 100644
index 1024a169571..00000000000
--- a/release/scripts/io/netrender/netrender.js
+++ /dev/null
@@ -1,146 +0,0 @@
-lastFrame = -1
-maxFrame = -1
-minFrame = -1
-
-function request(url, data)
-{
- xmlhttp = new XMLHttpRequest();
- xmlhttp.open("POST", url, false);
- xmlhttp.send(data);
- window.location.reload()
-}
-
-function edit(id, info)
-{
- request("/edit_" + id, info)
-}
-
-function clear_jobs()
-{
- var r=confirm("Also delete files on master?");
-
- if (r==true) {
- request('/clear', '{"clear":true}');
- } else {
- request('/clear', '{"clear":false}');
- }
-}
-
-function cancel_job(id)
-{
- var r=confirm("Also delete files on master?");
-
- if (r==true) {
- request('/cancel_' + id, '{"clear":true}');
- } else {
- request('/cancel_' + id, '{"clear":false}');
- }
-}
-
-function balance_edit(id, old_value)
-{
- var new_value = prompt("New limit", old_value);
- if (new_value != null && new_value != "") {
- request("/balance_limit", '{"' + id + '":"' + new_value + '"}');
- }
-}
-
-function balance_enable(id, value)
-{
- request("/balance_enable", '{"' + id + '":' + value + "}");
-}
-
-function showThumb(job, frame)
-{
- if (lastFrame != -1) {
- if (maxFrame != -1 && minFrame != -1) {
- if (frame >= minFrame && frame <= maxFrame) {
- for(i = minFrame; i <= maxFrame; i=i+1) {
- toggleThumb(job, i);
- }
- minFrame = -1;
- maxFrame = -1;
- lastFrame = -1;
- } else if (frame > maxFrame) {
- for(i = maxFrame+1; i <= frame; i=i+1) {
- toggleThumb(job, i);
- }
- maxFrame = frame;
- lastFrame = frame;
- } else {
- for(i = frame; i <= minFrame-1; i=i+1) {
- toggleThumb(job, i);
- }
- minFrame = frame;
- lastFrame = frame;
- }
- } else if (frame == lastFrame) {
- toggleThumb(job, frame);
- } else if (frame < lastFrame) {
- minFrame = frame;
- maxFrame = lastFrame;
-
- for(i = minFrame; i <= maxFrame-1; i=i+1) {
- toggleThumb(job, i);
- }
- lastFrame = frame;
- } else {
- minFrame = lastFrame;
- maxFrame = frame;
-
- for(i = minFrame+1; i <= maxFrame; i=i+1) {
- toggleThumb(job, i);
- }
- lastFrame = frame;
- }
- } else {
- toggleThumb(job, frame);
- }
-}
-
-function toggleThumb(job, frame)
-{
- img = document.images["thumb" + frame];
- url = "/thumb_" + job + "_" + frame + ".jpg"
-
- if (img.style.display == "block") {
- img.style.display = "none";
- img.src = "";
- lastFrame = -1;
- } else {
- img.src = url;
- img.style.display = "block";
- lastFrame = frame;
- }
-}
-
-function returnObjById( id )
-{
- if (document.getElementById)
- var returnVar = document.getElementById(id);
- else if (document.all)
- var returnVar = document.all[id];
- else if (document.layers)
- var returnVar = document.layers[id];
- return returnVar;
-}
-
-function toggleDisplay( className, value1, value2 )
-{
- style = getStyle(className)
-
- if (style.style["display"] == value1) {
- style.style["display"] = value2;
- } else {
- style.style["display"] = value1;
- }
-}
-
-function getStyle(className) {
- var classes = document.styleSheets[0].rules || document.styleSheets[0].cssRules
- for(var x=0;x<classes.length;x++) {
- if(classes[x].selectorText==className) {
- return classes[x];
- }
- }
-} \ No newline at end of file
diff --git a/release/scripts/io/netrender/operators.py b/release/scripts/io/netrender/operators.py
deleted file mode 100644
index f2c2fda7bde..00000000000
--- a/release/scripts/io/netrender/operators.py
+++ /dev/null
@@ -1,564 +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 #####
-
-import bpy
-import sys, os
-import http, http.client, http.server, urllib, socket
-import webbrowser
-import json
-
-import netrender
-from netrender.utils import *
-import netrender.client as client
-import netrender.model
-import netrender.versioning as versioning
-
-class RENDER_OT_netslave_bake(bpy.types.Operator):
- '''NEED DESCRIPTION'''
- bl_idname = "render.netslavebake"
- bl_label = "Bake all in file"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- filename = bpy.data.filepath
- path, name = os.path.split(filename)
- root, ext = os.path.splitext(name)
- default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
- relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
-
- # Force all point cache next to the blend file
- for object in bpy.data.objects:
- for modifier in object.modifiers:
- if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
- modifier.settings.path = relative_path
- bpy.ops.fluid.bake({"active_object": object, "scene": scene})
- elif modifier.type == "CLOTH":
- modifier.point_cache.frame_step = 1
- modifier.point_cache.use_disk_cache = True
- modifier.point_cache.use_external = False
- elif modifier.type == "SOFT_BODY":
- modifier.point_cache.frame_step = 1
- modifier.point_cache.use_disk_cache = True
- modifier.point_cache.use_external = False
- elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
- modifier.domain_settings.point_cache.use_step = 1
- modifier.domain_settings.point_cache.use_disk_cache = True
- modifier.domain_settings.point_cache.use_external = False
-
- # particles modifier are stupid and don't contain data
- # we have to go through the object property
- for psys in object.particle_systems:
- psys.point_cache.use_step = 1
- psys.point_cache.use_disk_cache = True
- psys.point_cache.use_external = False
- psys.point_cache.filepath = relative_path
-
- bpy.ops.ptcache.bake_all()
-
- #bpy.ops.wm.save_mainfile(filepath = path + os.sep + root + "_baked.blend")
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientanim(bpy.types.Operator):
- '''Start rendering an animation on network'''
- bl_idname = "render.netclientanim"
- bl_label = "Animation on network"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- # Sending file
- scene.network_render.job_id = client.clientSendJob(conn, scene, True)
- conn.close()
-
- bpy.ops.render.render('INVOKE_AREA', animation=True)
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientrun(bpy.types.Operator):
- '''Start network rendering service'''
- bl_idname = "render.netclientstart"
- bl_label = "Start Service"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- bpy.ops.render.render('INVOKE_AREA', animation=True)
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientsend(bpy.types.Operator):
- '''Send Render Job to the Network'''
- bl_idname = "render.netclientsend"
- bl_label = "Send job"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- try:
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- # Sending file
- scene.network_render.job_id = client.clientSendJob(conn, scene, True)
- conn.close()
- self.report('INFO', "Job sent to master")
- except Exception as err:
- self.report('ERROR', str(err))
-
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientsendframe(bpy.types.Operator):
- '''Send Render Job with current frame to the Network'''
- bl_idname = "render.netclientsendframe"
- bl_label = "Send current frame job"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- scene = context.scene
- netsettings = scene.network_render
-
- try:
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- # Sending file
- scene.network_render.job_id = client.clientSendJob(conn, scene, False)
- conn.close()
- self.report('INFO', "Job sent to master")
- except Exception as err:
- self.report('ERROR', str(err))
-
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientstatus(bpy.types.Operator):
- '''Refresh the status of the current jobs'''
- bl_idname = "render.netclientstatus"
- bl_label = "Client Status"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.request("GET", "/status")
-
- response = conn.getresponse()
- content = response.read()
- print( response.status, response.reason )
-
- jobs = (netrender.model.RenderJob.materialize(j) for j in json.loads(str(content, encoding='utf8')))
-
- while(len(netsettings.jobs) > 0):
- netsettings.jobs.remove(0)
-
- netrender.jobs = []
-
- for j in jobs:
- netrender.jobs.append(j)
- netsettings.jobs.add()
- job = netsettings.jobs[-1]
-
- j.results = j.framesStatus() # cache frame status
-
- job.name = j.name
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
- '''Operator documentation text, will be used for the operator tooltip and python docs.'''
- bl_idname = "render.netclientblacklistslave"
- bl_label = "Client Blacklist Slave"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
- if netsettings.active_slave_index >= 0:
-
- # deal with data
- slave = netrender.slaves.pop(netsettings.active_slave_index)
- netrender.blacklist.append(slave)
-
- # deal with rna
- netsettings.slaves_blacklist.add()
- netsettings.slaves_blacklist[-1].name = slave.name
-
- netsettings.slaves.remove(netsettings.active_slave_index)
- netsettings.active_slave_index = -1
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
- '''Operator documentation text, will be used for the operator tooltip and python docs.'''
- bl_idname = "render.netclientwhitelistslave"
- bl_label = "Client Whitelist Slave"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
- if netsettings.active_blacklisted_slave_index >= 0:
-
- # deal with data
- slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
- netrender.slaves.append(slave)
-
- # deal with rna
- netsettings.slaves.add()
- netsettings.slaves[-1].name = slave.name
-
- netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
- netsettings.active_blacklisted_slave_index = -1
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-
-class RENDER_OT_netclientslaves(bpy.types.Operator):
- '''Refresh status about available Render slaves'''
- bl_idname = "render.netclientslaves"
- bl_label = "Client Slaves"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.request("GET", "/slaves")
-
- response = conn.getresponse()
- content = response.read()
- print( response.status, response.reason )
-
- slaves = (netrender.model.RenderSlave.materialize(s) for s in json.loads(str(content, encoding='utf8')))
-
- while(len(netsettings.slaves) > 0):
- netsettings.slaves.remove(0)
-
- netrender.slaves = []
-
- for s in slaves:
- for i in range(len(netrender.blacklist)):
- slave = netrender.blacklist[i]
- if slave.id == s.id:
- netrender.blacklist[i] = s
- netsettings.slaves_blacklist[i].name = s.name
- break
- else:
- netrender.slaves.append(s)
-
- netsettings.slaves.add()
- slave = netsettings.slaves[-1]
- slave.name = s.name
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientcancel(bpy.types.Operator):
- '''Cancel the selected network rendering job.'''
- bl_idname = "render.netclientcancel"
- bl_label = "Client Cancel"
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- job = netrender.jobs[netsettings.active_job_index]
-
- conn.request("POST", cancelURL(job.id), json.dumps({'clear':False}))
-
- response = conn.getresponse()
- response.read()
- print( response.status, response.reason )
-
- netsettings.jobs.remove(netsettings.active_job_index)
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class RENDER_OT_netclientcancelall(bpy.types.Operator):
- '''Cancel all running network rendering jobs.'''
- bl_idname = "render.netclientcancelall"
- bl_label = "Client Cancel All"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.request("POST", "/clear", json.dumps({'clear':False}))
-
- response = conn.getresponse()
- response.read()
- print( response.status, response.reason )
-
- while(len(netsettings.jobs) > 0):
- netsettings.jobs.remove(0)
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class netclientdownload(bpy.types.Operator):
- '''Download render results from the network'''
- bl_idname = "render.netclientdownload"
- bl_label = "Client Download"
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return netsettings.active_job_index >= 0 and len(netsettings.jobs) > netsettings.active_job_index
-
- def execute(self, context):
- netsettings = context.scene.network_render
- rd = context.scene.render
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- job_id = netrender.jobs[netsettings.active_job_index].id
-
- conn.request("GET", "/status", headers={"job-id":job_id})
-
- response = conn.getresponse()
-
- if response.status != http.client.OK:
- self.report('ERROR', "Job ID %i not defined on master" % job_id)
- return {'ERROR'}
-
- content = response.read()
-
- job = netrender.model.RenderJob.materialize(json.loads(str(content, encoding='utf8')))
-
- conn.close()
-
- finished_frames = []
-
- nb_error = 0
- nb_missing = 0
-
- for frame in job.frames:
- if frame.status == DONE:
- finished_frames.append(frame.number)
- elif frame.status == ERROR:
- nb_error += 1
- else:
- nb_missing += 1
-
- if not finished_frames:
- return
-
- frame_ranges = []
-
- first = None
- last = None
-
- for i in range(len(finished_frames)):
- current = finished_frames[i]
-
- if not first:
- first = current
- last = current
- elif last + 1 == current:
- last = current
-
- if last + 1 < current or i + 1 == len(finished_frames):
- if first < last:
- frame_ranges.append((first, last))
- else:
- frame_ranges.append((first,))
-
- first = current
- last = current
-
- getResults(netsettings.server_address, netsettings.server_port, job_id, job.resolution[0], job.resolution[1], job.resolution[2], frame_ranges)
-
- if nb_error and nb_missing:
- self.report('ERROR', "Results downloaded but skipped %i frames with errors and %i unfinished frames" % (nb_error, nb_missing))
- elif nb_error:
- self.report('ERROR', "Results downloaded but skipped %i frames with errors" % nb_error)
- elif nb_missing:
- self.report('WARNING', "Results downloaded but skipped %i unfinished frames" % nb_missing)
- else:
- self.report('INFO', "All results downloaded")
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class netclientscan(bpy.types.Operator):
- '''Listen on network for master server broadcasting its address and port.'''
- bl_idname = "render.netclientscan"
- bl_label = "Client Scan"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- address, port = clientScan(self.report)
-
- if address:
- scene = context.scene
- netsettings = scene.network_render
- netsettings.server_address = address
- netsettings.server_port = port
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-class netclientvcsguess(bpy.types.Operator):
- '''Guess VCS setting for the current file'''
- bl_idname = "render.netclientvcsguess"
- bl_label = "VCS Guess"
-
- @classmethod
- def poll(cls, context):
- return True
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
- system = versioning.SYSTEMS.get(netsettings.vcs_system, None)
-
- if system:
- wpath, name = os.path.split(os.path.abspath(bpy.data.filepath))
-
- rpath = system.path(wpath)
- revision = system.revision(wpath)
-
- netsettings.vcs_wpath = wpath
- netsettings.vcs_rpath = rpath
- netsettings.vcs_revision = revision
-
-
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
-
-
-class netclientweb(bpy.types.Operator):
- '''Open new window with information about running rendering jobs'''
- bl_idname = "render.netclientweb"
- bl_label = "Open Master Monitor"
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return netsettings.server_address != "[default]"
-
- def execute(self, context):
- netsettings = context.scene.network_render
-
-
- # open connection to make sure server exists
- conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
-
- if conn:
- conn.close()
-
- webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
-
- return {'FINISHED'}
-
- def invoke(self, context, event):
- return self.execute(context)
diff --git a/release/scripts/io/netrender/repath.py b/release/scripts/io/netrender/repath.py
deleted file mode 100644
index 3ac9636b628..00000000000
--- a/release/scripts/io/netrender/repath.py
+++ /dev/null
@@ -1,150 +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 #####
-
-import sys, os
-import subprocess
-
-import bpy
-
-from netrender.utils import *
-import netrender.model
-
-BLENDER_PATH = sys.argv[0]
-
-def reset(job):
- main_file = job.files[0]
-
- job_full_path = main_file.filepath
-
- if os.path.exists(job_full_path + ".bak"):
- os.remove(job_full_path) # repathed file
- os.renames(job_full_path + ".bak", job_full_path)
-
-def update(job):
- paths = []
-
- main_file = job.files[0]
-
- job_full_path = main_file.filepath
-
-
- path, ext = os.path.splitext(job_full_path)
-
- new_path = path + ".remap" + ext
-
- # Disable for now. Partial repath should work anyway
- #all = main_file.filepath != main_file.original_path
- all = False
-
- for rfile in job.files[1:]:
- if all or rfile.original_path != rfile.filepath:
- paths.append(rfile.original_path)
- paths.append(rfile.filepath)
-
- # Only update if needed
- if paths:
- process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-P", __file__, "--", new_path] + paths, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- process.wait()
-
- os.renames(job_full_path, job_full_path + ".bak")
- os.renames(new_path, job_full_path)
-
-def process(paths):
- def processPointCache(point_cache):
- point_cache.use_external = False
-
- def processFluid(fluid):
- new_path = path_map.get(fluid.filepath, None)
- if new_path:
- fluid.path = new_path
-
- path_map = {}
- for i in range(0, len(paths), 2):
- # special case for point cache
- if paths[i].endswith(".bphys"):
- pass # Don't need them in the map, they all use the default external path
- # NOTE: This is probably not correct all the time, need to be fixed.
- # special case for fluids
- elif paths[i].endswith(".bobj.gz"):
- path_map[os.path.split(paths[i])[0]] = os.path.split(paths[i+1])[0]
- else:
- path_map[os.path.split(paths[i])[1]] = paths[i+1]
-
- # TODO original paths aren't really the orignal path (they are the normalized path
- # so we repath using the filenames only.
-
- ###########################
- # LIBRARIES
- ###########################
- for lib in bpy.data.libraries:
- file_path = bpy.path.abspath(lib.filepath)
- new_path = path_map.get(os.path.split(file_path)[1], None)
- if new_path:
- lib.filepath = new_path
-
- ###########################
- # IMAGES
- ###########################
- for image in bpy.data.images:
- if image.source == "FILE" and not image.packed_file:
- file_path = bpy.path.abspath(image.filepath)
- new_path = path_map.get(os.path.split(file_path)[1], None)
- if new_path:
- image.filepath = new_path
-
-
- ###########################
- # FLUID + POINT CACHE
- ###########################
- for object in bpy.data.objects:
- for modifier in object.modifiers:
- if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
- processFluid(settings)
- elif modifier.type == "CLOTH":
- processPointCache(modifier.point_cache)
- elif modifier.type == "SOFT_BODY":
- processPointCache(modifier.point_cache)
- elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
- processPointCache(modifier.domain_settings.point_cache_low)
- if modifier.domain_settings.use_high_resolution:
- processPointCache(modifier.domain_settings.point_cache_high)
- elif modifier.type == "MULTIRES" and modifier.is_external:
- file_path = bpy.path.abspath(modifier.filepath)
- new_path = path_map.get(file_path, None)
- if new_path:
- modifier.filepath = new_path
-
- # particles modifier are stupid and don't contain data
- # we have to go through the object property
- for psys in object.particle_systems:
- processPointCache(psys.point_cache)
-
-
-if __name__ == "__main__":
- try:
- i = sys.argv.index("--")
- except:
- i = 0
-
- if i:
- new_path = sys.argv[i+1]
- args = sys.argv[i+2:]
-
- process(args)
-
- bpy.ops.wm.save_as_mainfile(filepath=new_path, check_existing=False)
diff --git a/release/scripts/io/netrender/slave.py b/release/scripts/io/netrender/slave.py
deleted file mode 100644
index b05de0afeb9..00000000000
--- a/release/scripts/io/netrender/slave.py
+++ /dev/null
@@ -1,349 +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 #####
-
-import sys, os, platform, shutil
-import http, http.client, http.server, urllib
-import subprocess, time
-import json
-
-import bpy
-
-from netrender.utils import *
-import netrender.model
-import netrender.repath
-import netrender.thumbnail as thumbnail
-
-BLENDER_PATH = sys.argv[0]
-
-CANCEL_POLL_SPEED = 2
-MAX_TIMEOUT = 10
-INCREMENT_TIMEOUT = 1
-MAX_CONNECT_TRY = 10
-try:
- system = platform.system()
-except UnicodeDecodeError:
- import sys
- system = sys.platform
-
-if system in ('Windows', 'win32') and platform.version() >= '5': # Error mode is only available on Win2k or higher, that's version 5
- import ctypes
- def SetErrorMode():
- val = ctypes.windll.kernel32.SetErrorMode(0x0002)
- ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
- return val
-
- def RestoreErrorMode(val):
- ctypes.windll.kernel32.SetErrorMode(val)
-else:
- def SetErrorMode():
- return 0
-
- def RestoreErrorMode(val):
- pass
-
-def clearSlave(path):
- shutil.rmtree(path)
-
-def slave_Info():
- sysname, nodename, release, version, machine, processor = platform.uname()
- slave = netrender.model.RenderSlave()
- slave.name = nodename
- slave.stats = sysname + " " + release + " " + machine + " " + processor
- return slave
-
-def testCancel(conn, job_id, frame_number):
- conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
-
- # canceled if job isn't found anymore
- if responseStatus(conn) == http.client.NO_CONTENT:
- return True
- else:
- return False
-
-def testFile(conn, job_id, slave_id, rfile, JOB_PREFIX, main_path = None):
- job_full_path = prefixPath(JOB_PREFIX, rfile.filepath, main_path)
-
- found = os.path.exists(job_full_path)
-
- if found and rfile.signature != None:
- found_signature = hashFile(job_full_path)
- found = found_signature == rfile.signature
-
- if not found:
- print("Found file %s at %s but signature mismatch!" % (rfile.filepath, job_full_path))
- job_full_path = prefixPath(JOB_PREFIX, rfile.filepath, main_path, force = True)
-
- if not found:
- # Force prefix path if not found
- job_full_path = prefixPath(JOB_PREFIX, rfile.filepath, main_path, force = True)
- temp_path = os.path.join(JOB_PREFIX, "slave.temp")
- conn.request("GET", fileURL(job_id, rfile.index), headers={"slave-id":slave_id})
- response = conn.getresponse()
-
- if response.status != http.client.OK:
- return None # file for job not returned by server, need to return an error code to server
-
- f = open(temp_path, "wb")
- buf = response.read(1024)
-
- while buf:
- f.write(buf)
- buf = response.read(1024)
-
- f.close()
-
- os.renames(temp_path, job_full_path)
-
- rfile.filepath = job_full_path
-
- return job_full_path
-
-def breakable_timeout(timeout):
- for i in range(timeout):
- time.sleep(1)
- if engine.test_break():
- break
-
-def render_slave(engine, netsettings, threads):
- timeout = 1
-
- bisleep = BreakableIncrementedSleep(INCREMENT_TIMEOUT, 1, MAX_TIMEOUT, engine.test_break)
-
- engine.update_stats("", "Network render node initiation")
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port)
-
- if not conn:
- timeout = 1
- print("Connection failed, will try connecting again at most %i times" % MAX_CONNECT_TRY)
- bisleep.reset()
-
- for i in range(MAX_CONNECT_TRY):
- bisleep.sleep()
-
- conn = clientConnection(netsettings.server_address, netsettings.server_port)
-
- if conn or engine.test_break():
- break
-
- print("Retry %i failed, waiting %is before retrying" % (i + 1, bisleep.current))
-
- if conn:
- conn.request("POST", "/slave", json.dumps(slave_Info().serialize()))
- response = conn.getresponse()
- response.read()
-
- slave_id = response.getheader("slave-id")
-
- NODE_PREFIX = os.path.join(bpy.path.abspath(netsettings.path), "slave_" + slave_id)
- if not os.path.exists(NODE_PREFIX):
- os.mkdir(NODE_PREFIX)
-
- engine.update_stats("", "Network render connected to master, waiting for jobs")
-
- while not engine.test_break():
- conn.request("GET", "/job", headers={"slave-id":slave_id})
- response = conn.getresponse()
-
- if response.status == http.client.OK:
- bisleep.reset()
-
- job = netrender.model.RenderJob.materialize(json.loads(str(response.read(), encoding='utf8')))
- engine.update_stats("", "Network render processing job from master")
-
- JOB_PREFIX = os.path.join(NODE_PREFIX, "job_" + job.id)
- if not os.path.exists(JOB_PREFIX):
- os.mkdir(JOB_PREFIX)
-
- # set tempdir for fsaa temp files
- # have to set environ var because render is done in a subprocess and that's the easiest way to propagate the setting
- os.environ["TMP"] = JOB_PREFIX
-
-
- if job.type == netrender.model.JOB_BLENDER:
- job_path = job.files[0].filepath # path of main file
- main_path, main_file = os.path.split(job_path)
-
- job_full_path = testFile(conn, job.id, slave_id, job.files[0], JOB_PREFIX)
- print("Fullpath", job_full_path)
- print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
-
- for rfile in job.files[1:]:
- testFile(conn, job.id, slave_id, rfile, JOB_PREFIX, main_path)
- print("\t", rfile.filepath)
-
- netrender.repath.update(job)
-
- engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
- elif job.type == netrender.model.JOB_VCS:
- if not job.version_info:
- # Need to return an error to server, incorrect job type
- pass
-
- job_path = job.files[0].filepath # path of main file
- main_path, main_file = os.path.split(job_path)
-
- job.version_info.update()
-
- # For VCS jobs, file path is relative to the working copy path
- job_full_path = os.path.join(job.version_info.wpath, job_path)
-
- engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
-
- # announce log to master
- logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
- conn.request("POST", "/log", bytes(json.dumps(logfile.serialize()), encoding='utf8'))
- response = conn.getresponse()
- response.read()
-
-
- first_frame = job.frames[0].number
-
- # start render
- start_t = time.time()
-
- if job.rendersWithBlender():
- frame_args = []
-
- for frame in job.frames:
- print("frame", frame.number)
- frame_args += ["-f", str(frame.number)]
-
- val = SetErrorMode()
- process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-t", str(threads), "-o", os.path.join(JOB_PREFIX, "######"), "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- RestoreErrorMode(val)
- elif job.type == netrender.model.JOB_PROCESS:
- command = job.frames[0].command
- val = SetErrorMode()
- process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- RestoreErrorMode(val)
-
- headers = {"slave-id":slave_id}
-
- cancelled = False
- stdout = bytes()
- run_t = time.time()
- while not cancelled and process.poll() is None:
- stdout += process.stdout.read(1024)
- current_t = time.time()
- cancelled = engine.test_break()
- if current_t - run_t > CANCEL_POLL_SPEED:
-
- # update logs if needed
- if stdout:
- # (only need to update on one frame, they are linked
- conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
- response = conn.getresponse()
- response.read()
-
- # Also output on console
- if netsettings.use_slave_output_log:
- print(str(stdout, encoding='utf8'), end="")
-
- stdout = bytes()
-
- run_t = current_t
- if testCancel(conn, job.id, first_frame):
- cancelled = True
-
- if job.type == netrender.model.JOB_BLENDER:
- netrender.repath.reset(job)
-
- # read leftovers if needed
- stdout += process.stdout.read()
-
- if cancelled:
- # kill process if needed
- if process.poll() is None:
- try:
- process.terminate()
- except OSError:
- pass
- continue # to next frame
-
- # flush the rest of the logs
- if stdout:
- # Also output on console
- if netsettings.use_slave_thumb:
- print(str(stdout, encoding='utf8'), end="")
-
- # (only need to update on one frame, they are linked
- conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
- if responseStatus(conn) == http.client.NO_CONTENT:
- continue
-
- total_t = time.time() - start_t
-
- avg_t = total_t / len(job.frames)
-
- status = process.returncode
-
- print("status", status)
-
- headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
-
-
- if status == 0: # non zero status is error
- headers["job-result"] = str(DONE)
- for frame in job.frames:
- headers["job-frame"] = str(frame.number)
- if job.hasRenderResult():
- # send image back to server
-
- filename = os.path.join(JOB_PREFIX, "%06d.exr" % frame.number)
-
- # thumbnail first
- if netsettings.use_slave_thumb:
- thumbname = thumbnail.generate(filename)
-
- if thumbname:
- f = open(thumbname, 'rb')
- conn.request("PUT", "/thumb", f, headers=headers)
- f.close()
- responseStatus(conn)
-
- f = open(filename, 'rb')
- conn.request("PUT", "/render", f, headers=headers)
- f.close()
- if responseStatus(conn) == http.client.NO_CONTENT:
- continue
-
- elif job.type == netrender.model.JOB_PROCESS:
- conn.request("PUT", "/render", headers=headers)
- if responseStatus(conn) == http.client.NO_CONTENT:
- continue
- else:
- headers["job-result"] = str(ERROR)
- for frame in job.frames:
- headers["job-frame"] = str(frame.number)
- # send error result back to server
- conn.request("PUT", "/render", headers=headers)
- if responseStatus(conn) == http.client.NO_CONTENT:
- continue
-
- engine.update_stats("", "Network render connected to master, waiting for jobs")
- else:
- bisleep.sleep()
-
- conn.close()
-
- if netsettings.use_slave_clear:
- clearSlave(NODE_PREFIX)
-
-if __name__ == "__main__":
- pass
diff --git a/release/scripts/io/netrender/thumbnail.py b/release/scripts/io/netrender/thumbnail.py
deleted file mode 100644
index 2ead6e82745..00000000000
--- a/release/scripts/io/netrender/thumbnail.py
+++ /dev/null
@@ -1,81 +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 #####
-
-import sys, os
-import subprocess
-
-import bpy
-
-def generate(filename, external=True):
- if external:
- process = subprocess.Popen([sys.argv[0], "-b", "-noaudio", "-P", __file__, "--", filename], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- while process.poll() is None:
- process.stdout.read(1024) # empty buffer to be sure
- process.stdout.read()
-
- return _thumbname(filename)
- else:
- return _internal(filename)
-
-def _thumbname(filename):
- root = os.path.splitext(filename)[0]
- return root + ".jpg"
-
-def _internal(filename):
- imagename = os.path.split(filename)[1]
- thumbname = _thumbname(filename)
-
- if os.path.exists(thumbname):
- return thumbname
-
- if bpy:
- scene = bpy.data.scenes[0] # FIXME, this is dodgy!
- scene.render.file_format = "JPEG"
- scene.render.file_quality = 90
-
- # remove existing image, if there's a leftover (otherwise open changes the name)
- if imagename in bpy.data.images:
- img = bpy.data.images[imagename]
- bpy.data.images.remove(img)
-
- bpy.ops.image.open(filepath=filename)
- img = bpy.data.images[imagename]
-
- img.save_render(thumbname, scene=scene)
-
- img.user_clear()
- bpy.data.images.remove(img)
-
- try:
- process = subprocess.Popen(["convert", thumbname, "-resize", "300x300", thumbname])
- process.wait()
- return thumbname
- except Exception as exp:
- print("Error while generating thumbnail")
- print(exp)
-
- return None
-
-if __name__ == "__main__":
- import bpy
- try:
- start = sys.argv.index("--") + 1
- except ValueError:
- start = 0
- for filename in sys.argv[start:]:
- generate(filename, external=False)
diff --git a/release/scripts/io/netrender/ui.py b/release/scripts/io/netrender/ui.py
deleted file mode 100644
index 343c60e7865..00000000000
--- a/release/scripts/io/netrender/ui.py
+++ /dev/null
@@ -1,550 +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 #####
-
-import bpy
-import sys, os
-import http, http.client, http.server, urllib
-import subprocess, shutil, time, hashlib
-
-import netrender
-import netrender.slave as slave
-import netrender.master as master
-
-from netrender.utils import *
-
-VERSION = b"0.3"
-
-PATH_PREFIX = "/tmp/"
-
-QUEUED = 0
-DISPATCHED = 1
-DONE = 2
-ERROR = 3
-
-LAST_ADDRESS_TEST = 0
-
-def base_poll(cls, context):
- rd = context.scene.render
- return (rd.use_game_engine==False) and (rd.engine in cls.COMPAT_ENGINES)
-
-
-def init_file():
- if netrender.init_file != bpy.data.filepath:
- netrender.init_file = bpy.data.filepath
- netrender.init_data = True
- netrender.valid_address = False
-
-def init_data(netsettings):
- init_file()
-
- if netrender.init_data:
- netrender.init_data = False
-
- netsettings.active_slave_index = 0
- while(len(netsettings.slaves) > 0):
- netsettings.slaves.remove(0)
-
- netsettings.active_blacklisted_slave_index = 0
- while(len(netsettings.slaves_blacklist) > 0):
- netsettings.slaves_blacklist.remove(0)
-
- netsettings.active_job_index = 0
- while(len(netsettings.jobs) > 0):
- netsettings.jobs.remove(0)
-
-def verify_address(netsettings):
- global LAST_ADDRESS_TEST
- init_file()
-
- if LAST_ADDRESS_TEST + 30 < time.time():
- LAST_ADDRESS_TEST = time.time()
-
- try:
- conn = clientConnection(netsettings.server_address, netsettings.server_port, scan = False, timeout = 1)
- except:
- conn = None
-
- if conn:
- netrender.valid_address = True
- conn.close()
- else:
- netrender.valid_address = False
-
- return netrender.valid_address
-
-class NeedValidAddress():
- @classmethod
- def poll(cls, context):
- return super().poll(context) and verify_address(context.scene.network_render)
-
-class NetRenderButtonsPanel():
- bl_space_type = "PROPERTIES"
- bl_region_type = "WINDOW"
- bl_context = "render"
- # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
-
- @classmethod
- def poll(cls, context):
- rd = context.scene.render
- return rd.engine == 'NET_RENDER' and rd.use_game_engine == False
-
-# Setting panel, use in the scene for now.
-class RENDER_PT_network_settings(NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Network Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- return super().poll(context)
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- verify_address(netsettings)
-
- layout.prop(netsettings, "mode", expand=True)
-
- if netsettings.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
- layout.operator("render.netclientstart", icon='PLAY')
-
- layout.prop(netsettings, "path")
-
- split = layout.split(percentage=0.7)
-
- col = split.column()
- col.label(text="Server Address:")
- col.prop(netsettings, "server_address", text="")
-
- col = split.column()
- col.label(text="Port:")
- col.prop(netsettings, "server_port", text="")
-
- if netsettings.mode != "RENDER_MASTER":
- layout.operator("render.netclientscan", icon='FILE_REFRESH', text="")
-
- if not netrender.valid_address:
- layout.label(text="No master at specified address")
-
- layout.operator("render.netclientweb", icon='QUESTION')
-
-class RENDER_PT_network_slave_settings(NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Slave Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- scene = context.scene
- return super().poll(context) and scene.network_render.mode == "RENDER_SLAVE"
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- rd = scene.render
- netsettings = scene.network_render
-
- layout.prop(netsettings, "use_slave_clear")
- layout.prop(netsettings, "use_slave_thumb")
- layout.prop(netsettings, "use_slave_output_log")
- layout.label(text="Threads:")
- layout.prop(rd, "threads_mode", expand=True)
- sub = layout.column()
- sub.enabled = rd.threads_mode == 'FIXED'
- sub.prop(rd, "threads")
-
-class RENDER_PT_network_master_settings(NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Master Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- scene = context.scene
- return super().poll(context) and scene.network_render.mode == "RENDER_MASTER"
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- layout.prop(netsettings, "use_master_broadcast")
- layout.prop(netsettings, "use_master_clear")
-
-class RENDER_PT_network_job(NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Job Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- scene = context.scene
- return super().poll(context) and scene.network_render.mode == "RENDER_CLIENT"
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- verify_address(netsettings)
-
- if netsettings.server_address != "[default]":
- layout.operator("render.netclientanim", icon='RENDER_ANIMATION')
- layout.operator("render.netclientsend", icon='FILE_BLEND')
- layout.operator("render.netclientsendframe", icon='RENDER_STILL')
- if netsettings.job_id:
- row = layout.row()
- row.operator("render.render", text="Get Image", icon='RENDER_STILL')
- row.operator("render.render", text="Get Animation", icon='RENDER_ANIMATION').animation = True
-
- split = layout.split(percentage=0.3)
-
- col = split.column()
- col.label(text="Type:")
- col.label(text="Name:")
- col.label(text="Category:")
-
- col = split.column()
- col.prop(netsettings, "job_type", text="")
- col.prop(netsettings, "job_name", text="")
- col.prop(netsettings, "job_category", text="")
-
- row = layout.row()
- row.prop(netsettings, "priority")
- row.prop(netsettings, "chunks")
-
-class RENDER_PT_network_job_vcs(NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "VCS Job Settings"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- scene = context.scene
- return (super().poll(context)
- and scene.network_render.mode == "RENDER_CLIENT"
- and scene.network_render.job_type == "JOB_VCS")
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- layout.operator("render.netclientvcsguess", icon='FILE_REFRESH', text="")
-
- layout.prop(netsettings, "vcs_system")
- layout.prop(netsettings, "vcs_revision")
- layout.prop(netsettings, "vcs_rpath")
- layout.prop(netsettings, "vcs_wpath")
-
-class RENDER_PT_network_slaves(NeedValidAddress, NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Slaves Status"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return super().poll(context) and netsettings.mode == "RENDER_CLIENT"
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- row = layout.row()
- row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
-
- sub = row.column(align=True)
- sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
- sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
-
- if len(netrender.slaves) > netsettings.active_slave_index >= 0:
- layout.separator()
-
- slave = netrender.slaves[netsettings.active_slave_index]
-
- layout.label(text="Name: " + slave.name)
- layout.label(text="Address: " + slave.address[0])
- layout.label(text="Seen: " + time.ctime(slave.last_seen))
- layout.label(text="Stats: " + slave.stats)
-
-class RENDER_PT_network_slaves_blacklist(NeedValidAddress, NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Slaves Blacklist"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return super().poll(context) and netsettings.mode == "RENDER_CLIENT"
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- row = layout.row()
- row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
-
- sub = row.column(align=True)
- sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
-
- if len(netrender.blacklist) > netsettings.active_blacklisted_slave_index >= 0:
- layout.separator()
-
- slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
-
- layout.label(text="Name: " + slave.name)
- layout.label(text="Address: " + slave.address[0])
- layout.label(text="Seen: " + time.ctime(slave.last_seen))
- layout.label(text="Stats: " + slave.stats)
-
-class RENDER_PT_network_jobs(NeedValidAddress, NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Jobs"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return super().poll(context) and netsettings.mode == "RENDER_CLIENT"
-
- def draw(self, context):
- layout = self.layout
-
- scene = context.scene
- netsettings = scene.network_render
-
- row = layout.row()
- row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
-
- sub = row.column(align=True)
- sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
- sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
- sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
- sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
-
- if len(netrender.jobs) > netsettings.active_job_index >= 0:
- layout.separator()
-
- job = netrender.jobs[netsettings.active_job_index]
-
- layout.label(text="Name: %s" % job.name)
- layout.label(text="Length: %04i" % len(job))
- layout.label(text="Done: %04i" % job.results[DONE])
- layout.label(text="Error: %04i" % job.results[ERROR])
-
-import properties_render
-class RENDER_PT_network_output(NeedValidAddress, NetRenderButtonsPanel, bpy.types.Panel):
- bl_label = "Output"
- COMPAT_ENGINES = {'NET_RENDER'}
-
- @classmethod
- def poll(cls, context):
- netsettings = context.scene.network_render
- return super().poll(context) and netsettings.mode == "RENDER_CLIENT"
-
- draw = properties_render.RENDER_PT_output.draw
-
-
-def addProperties():
- class NetRenderSettings(bpy.types.IDPropertyGroup):
- pass
-
- class NetRenderSlave(bpy.types.IDPropertyGroup):
- pass
-
- class NetRenderJob(bpy.types.IDPropertyGroup):
- pass
-
- bpy.utils.register_class(NetRenderSettings)
- bpy.utils.register_class(NetRenderSlave)
- bpy.utils.register_class(NetRenderJob)
-
- from bpy.props import PointerProperty, StringProperty, BoolProperty, EnumProperty, IntProperty, CollectionProperty
- bpy.types.Scene.network_render = PointerProperty(type=NetRenderSettings, name="Network Render", description="Network Render Settings")
-
- NetRenderSettings.server_address = StringProperty(
- name="Server address",
- description="IP or name of the master render server",
- maxlen = 128,
- default = "[default]")
-
- NetRenderSettings.server_port = IntProperty(
- name="Server port",
- description="port of the master render server",
- default = 8000,
- min=1,
- max=65535)
-
- NetRenderSettings.use_master_broadcast = BoolProperty(
- name="Broadcast",
- description="broadcast master server address on local network",
- default = True)
-
- NetRenderSettings.use_slave_clear = BoolProperty(
- name="Clear on exit",
- description="delete downloaded files on exit",
- default = True)
-
- NetRenderSettings.use_slave_thumb = BoolProperty(
- name="Generate thumbnails",
- description="Generate thumbnails on slaves instead of master",
- default = False)
-
- NetRenderSettings.use_slave_output_log = BoolProperty(
- name="Output render log on console",
- description="Output render text log to console as well as sending it to the master",
- default = True)
-
- NetRenderSettings.use_master_clear = BoolProperty(
- name="Clear on exit",
- description="delete saved files on exit",
- default = False)
-
- default_path = os.environ.get("TEMP")
-
- if not default_path:
- if os.name == 'nt':
- default_path = "c:/tmp/"
- else:
- default_path = "/tmp/"
- elif not default_path.endswith(os.sep):
- default_path += os.sep
-
- NetRenderSettings.path = StringProperty(
- name="Path",
- description="Path for temporary files",
- maxlen = 128,
- default = default_path,
- subtype='FILE_PATH')
-
- NetRenderSettings.job_type = EnumProperty(
- items=(
- ("JOB_BLENDER", "Blender", "Standard Blender Job"),
- ("JOB_PROCESS", "Process", "Custom Process Job"),
- ("JOB_VCS", "VCS", "Version Control System Managed Job"),
- ),
- name="Job Type",
- description="Type of render job",
- default="JOB_BLENDER")
-
- NetRenderSettings.job_name = StringProperty(
- name="Job name",
- description="Name of the job",
- maxlen = 128,
- default = "[default]")
-
- NetRenderSettings.job_category = StringProperty(
- name="Job category",
- description="Category of the job",
- maxlen = 128,
- default = "")
-
- NetRenderSettings.chunks = IntProperty(
- name="Chunks",
- description="Number of frame to dispatch to each slave in one chunk",
- default = 5,
- min=1,
- max=65535)
-
- NetRenderSettings.priority = IntProperty(
- name="Priority",
- description="Priority of the job",
- default = 1,
- min=1,
- max=10)
-
- NetRenderSettings.vcs_wpath = StringProperty(
- name="Working Copy",
- description="Path of the local working copy",
- maxlen = 1024,
- default = "")
-
- NetRenderSettings.vcs_rpath = StringProperty(
- name="Remote Path",
- description="Path of the server copy (protocol specific)",
- maxlen = 1024,
- default = "")
-
- NetRenderSettings.vcs_revision = StringProperty(
- name="Revision",
- description="Revision for this job",
- maxlen = 256,
- default = "")
-
- NetRenderSettings.vcs_system = StringProperty(
- name="VCS",
- description="Version Control System",
- maxlen = 64,
- default = "Subversion")
-
- NetRenderSettings.job_id = StringProperty(
- name="Network job id",
- description="id of the last sent render job",
- maxlen = 64,
- default = "")
-
- NetRenderSettings.active_slave_index = IntProperty(
- name="Index of the active slave",
- description="",
- default = -1,
- min= -1,
- max=65535)
-
- NetRenderSettings.active_blacklisted_slave_index = IntProperty(
- name="Index of the active slave",
- description="",
- default = -1,
- min= -1,
- max=65535)
-
- NetRenderSettings.active_job_index = IntProperty(
- name="Index of the active job",
- description="",
- default = -1,
- min= -1,
- max=65535)
-
- NetRenderSettings.mode = EnumProperty(
- items=(
- ("RENDER_CLIENT", "Client", "Act as render client"),
- ("RENDER_MASTER", "Master", "Act as render master"),
- ("RENDER_SLAVE", "Slave", "Act as render slave"),
- ),
- name="Network mode",
- description="Mode of operation of this instance",
- default="RENDER_CLIENT")
-
- NetRenderSettings.slaves = CollectionProperty(type=NetRenderSlave, name="Slaves", description="")
- NetRenderSettings.slaves_blacklist = CollectionProperty(type=NetRenderSlave, name="Slaves Blacklist", description="")
- NetRenderSettings.jobs = CollectionProperty(type=NetRenderJob, name="Job List", description="")
-
- NetRenderSlave.name = StringProperty(
- name="Name of the slave",
- description="",
- maxlen = 64,
- default = "")
-
- NetRenderJob.name = StringProperty(
- name="Name of the job",
- description="",
- maxlen = 128,
- default = "")
diff --git a/release/scripts/io/netrender/utils.py b/release/scripts/io/netrender/utils.py
deleted file mode 100644
index ed9fb2de812..00000000000
--- a/release/scripts/io/netrender/utils.py
+++ /dev/null
@@ -1,313 +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 #####
-
-import sys, os
-import re
-import http, http.client, http.server, urllib, socket
-import subprocess, shutil, time, hashlib, zlib
-
-import netrender.model
-
-try:
- import bpy
-except:
- bpy = None
-
-VERSION = bytes("1.3", encoding='utf8')
-
-# Jobs status
-JOB_WAITING = 0 # before all data has been entered
-JOB_PAUSED = 1 # paused by user
-JOB_FINISHED = 2 # finished rendering
-JOB_QUEUED = 3 # ready to be dispatched
-
-JOB_STATUS_TEXT = {
- JOB_WAITING: "Waiting",
- JOB_PAUSED: "Paused",
- JOB_FINISHED: "Finished",
- JOB_QUEUED: "Queued"
- }
-
-
-# Frames status
-QUEUED = 0
-DISPATCHED = 1
-DONE = 2
-ERROR = 3
-
-FRAME_STATUS_TEXT = {
- QUEUED: "Queued",
- DISPATCHED: "Dispatched",
- DONE: "Done",
- ERROR: "Error"
- }
-
-class DirectoryContext:
- def __init__(self, path):
- self.path = path
-
- def __enter__(self):
- self.curdir = os.path.abspath(os.curdir)
- os.chdir(self.path)
-
- def __exit__(self, exc_type, exc_value, traceback):
- os.chdir(self.curdir)
-
-class BreakableIncrementedSleep:
- def __init__(self, increment, default_timeout, max_timeout, break_fct):
- self.increment = increment
- self.default = default_timeout
- self.max = max_timeout
- self.current = self.default
- self.break_fct = break_fct
-
- def reset(self):
- self.current = self.default
-
- def increase(self):
- self.current = min(self.current + self.increment, self.max)
-
- def sleep(self):
- for i in range(self.current):
- time.sleep(1)
- if self.break_fct():
- break
-
- self.increase()
-
-def responseStatus(conn):
- response = conn.getresponse()
- response.read()
- return response.status
-
-def reporting(report, message, errorType = None):
- if errorType:
- t = 'ERROR'
- else:
- t = 'INFO'
-
- if report:
- report(t, message)
- return None
- elif errorType:
- raise errorType(message)
- else:
- return None
-
-def clientScan(report = None):
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
- s.settimeout(30)
-
- s.bind(('', 8000))
-
- buf, address = s.recvfrom(64)
-
- address = address[0]
- port = int(str(buf, encoding='utf8'))
-
- reporting(report, "Master server found")
-
- return (address, port)
- except socket.timeout:
- reporting(report, "No master server on network", IOError)
-
- return ("", 8000) # return default values
-
-def clientConnection(address, port, report = None, scan = True, timeout = 5):
- if address == "[default]":
-# calling operator from python is fucked, scene isn't in context
-# if bpy:
-# bpy.ops.render.netclientscan()
-# else:
- if not scan:
- return None
-
- address, port = clientScan()
- if address == "":
- return None
-
- try:
- conn = http.client.HTTPConnection(address, port, timeout = timeout)
-
- if conn:
- if clientVerifyVersion(conn):
- return conn
- else:
- conn.close()
- reporting(report, "Incorrect master version", ValueError)
- except BaseException as err:
- if report:
- report('ERROR', str(err))
- return None
- else:
- print(err)
- return None
-
-def clientVerifyVersion(conn):
- conn.request("GET", "/version")
- response = conn.getresponse()
-
- if response.status != http.client.OK:
- conn.close()
- return False
-
- server_version = response.read()
-
- if server_version != VERSION:
- print("Incorrect server version!")
- print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
- return False
-
- return True
-
-def fileURL(job_id, file_index):
- return "/file_%s_%i" % (job_id, file_index)
-
-def logURL(job_id, frame_number):
- return "/log_%s_%i.log" % (job_id, frame_number)
-
-def renderURL(job_id, frame_number):
- return "/render_%s_%i.exr" % (job_id, frame_number)
-
-def cancelURL(job_id):
- return "/cancel_%s" % (job_id)
-
-def hashFile(path):
- f = open(path, "rb")
- value = hashData(f.read())
- f.close()
- return value
-
-def hashData(data):
- m = hashlib.md5()
- m.update(data)
- return m.hexdigest()
-
-
-def prefixPath(prefix_directory, file_path, prefix_path, force = False):
- if (os.path.isabs(file_path) or
- len(file_path) >= 3 and (file_path[1:3] == ":/" or file_path[1:3] == ":\\") or # Windows absolute path don't count as absolute on unix, have to handle them myself
- file_path[0] == "/" or file_path[0] == "\\"): # and vice versa
-
- # if an absolute path, make sure path exists, if it doesn't, use relative local path
- full_path = file_path
- if force or not os.path.exists(full_path):
- p, n = os.path.split(os.path.normpath(full_path))
-
- if prefix_path and p.startswith(prefix_path):
- if len(prefix_path) < len(p):
- directory = os.path.join(prefix_directory, p[len(prefix_path)+1:]) # +1 to remove separator
- if not os.path.exists(directory):
- os.mkdir(directory)
- else:
- directory = prefix_directory
- full_path = os.path.join(directory, n)
- else:
- full_path = os.path.join(prefix_directory, n)
- else:
- full_path = os.path.join(prefix_directory, file_path)
-
- return full_path
-
-def getResults(server_address, server_port, job_id, resolution_x, resolution_y, resolution_percentage, frame_ranges):
- if bpy.app.debug:
- print("=============================================")
- print("============= FETCHING RESULTS ==============")
-
- frame_arguments = []
- for r in frame_ranges:
- if len(r) == 2:
- frame_arguments.extend(["-s", str(r[0]), "-e", str(r[1]), "-a"])
- else:
- frame_arguments.extend(["-f", str(r[0])])
-
- filepath = os.path.join(bpy.app.tempdir, "netrender_temp.blend")
- bpy.ops.wm.save_as_mainfile(filepath=filepath, copy=True, check_existing=False)
-
- arguments = [sys.argv[0], "-b", "-noaudio", filepath, "-o", bpy.path.abspath(bpy.context.scene.render.filepath), "-P", __file__] + frame_arguments + ["--", "GetResults", server_address, str(server_port), job_id, str(resolution_x), str(resolution_y), str(resolution_percentage)]
- if bpy.app.debug:
- print("Starting subprocess:")
- print(" ".join(arguments))
-
- process = subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- while process.poll() is None:
- stdout = process.stdout.read(1024)
- if bpy.app.debug:
- print(str(stdout, encoding='utf-8'), end="")
-
-
- # read leftovers if needed
- stdout = process.stdout.read()
- if bpy.app.debug:
- print(str(stdout, encoding='utf-8'))
-
- os.remove(filepath)
-
- if bpy.app.debug:
- print("=============================================")
- return
-
-def _getResults(server_address, server_port, job_id, resolution_x, resolution_y, resolution_percentage):
- render = bpy.context.scene.render
-
- netsettings = bpy.context.scene.network_render
-
- netsettings.server_address = server_address
- netsettings.server_port = int(server_port)
- netsettings.job_id = job_id
-
- render.engine = 'NET_RENDER'
- render.resolution_x = int(resolution_x)
- render.resolution_y = int(resolution_y)
- render.resolution_percentage = int(resolution_percentage)
-
- render.use_full_sample = False
- render.use_compositing = False
- render.use_border = False
-
-
-def getFileInfo(filepath, infos):
- process = subprocess.Popen([sys.argv[0], "-b", "-noaudio", filepath, "-P", __file__, "--", "FileInfo"] + infos, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- stdout = bytes()
- while process.poll() is None:
- stdout += process.stdout.read(1024)
-
- # read leftovers if needed
- stdout += process.stdout.read()
-
- stdout = str(stdout, encoding="utf8")
-
- values = [eval(v[1:].strip()) for v in stdout.split("\n") if v.startswith("$")]
-
- return values
-
-
-if __name__ == "__main__":
- try:
- start = sys.argv.index("--") + 1
- except ValueError:
- start = 0
- action, *args = sys.argv[start:]
-
- if action == "FileInfo":
- for info in args:
- print("$", eval(info))
- elif action == "GetResults":
- _getResults(args[0], args[1], args[2], args[3], args[4], args[5])
diff --git a/release/scripts/io/netrender/versioning.py b/release/scripts/io/netrender/versioning.py
deleted file mode 100644
index d4f8522cce8..00000000000
--- a/release/scripts/io/netrender/versioning.py
+++ /dev/null
@@ -1,72 +0,0 @@
-import sys, os
-import re
-import subprocess
-
-from netrender.utils import *
-
-class AbstractVCS:
- name = "ABSTRACT VCS"
- def __init__(self):
- pass
-
- def update(self, info):
- """update(info)
- Update a working copy to the specified revision.
- If working copy doesn't exist, do a full get from server to create it.
- [info] model.VersioningInfo instance, specifies the working path, remote path and version number."""
- pass
-
- def revision(self, path):
- """revision(path)
- return the current revision of the specified working copy path"""
- pass
-
- def path(self, path):
- """path(path)
- return the remote path of the specified working copy path"""
- pass
-
-class Subversion(AbstractVCS):
- name = "Subversion"
- def __init__(self):
- super().__init__()
- self.version_exp = re.compile("([0-9]*)")
- self.path_exp = re.compile("URL: (.*)")
-
- def update(self, info):
- if not os.path.exists(info.wpath):
- base, folder = os.path.split(info.wpath)
-
- with DirectoryContext(base):
- subprocess.call(["svn", "co", "%s@%s" % (info.rpath, str(info.revision)), folder])
- else:
- with DirectoryContext(info.wpath):
- subprocess.call(["svn", "up", "--accept", "theirs-full", "-r", str(info.revision)])
-
- def revision(self, path):
- if not os.path.exists(path):
- return
-
- with DirectoryContext(path):
- stdout = subprocess.check_output(["svnversion"])
-
- match = self.version_exp.match(str(stdout, encoding="utf-8"))
-
- if match:
- return match.group(1)
-
- def path(self, path):
- if not os.path.exists(path):
- return
-
- with DirectoryContext(path):
- stdout = subprocess.check_output(["svn", "info"])
-
- match = self.path_exp.search(str(stdout, encoding="utf-8"))
-
- if match:
- return match.group(1)
-
-SYSTEMS = {
- Subversion.name: Subversion()
- }
diff --git a/release/scripts/modules/add_object_utils.py b/release/scripts/modules/add_object_utils.py
index 10707734bc4..1cf7fc2f4d5 100644
--- a/release/scripts/modules/add_object_utils.py
+++ b/release/scripts/modules/add_object_utils.py
@@ -83,6 +83,19 @@ def object_data_add(context, obdata, operator=None):
obj_act = scene.objects.active
+ # XXX
+ # caused because entering editmodedoes not add a empty undo slot!
+ if context.user_preferences.edit.use_enter_edit_mode:
+ if not (obj_act and obj_act.mode == 'EDIT' and obj_act.type == obj_new.type):
+ _obdata = bpy.data.meshes.new(obdata.name)
+ obj_act = bpy.data.objects.new(_obdata.name, _obdata)
+ obj_act.matrix_world = obj_new.matrix_world
+ scene.objects.link(obj_act)
+ scene.objects.active = obj_act
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.ed.undo_push(message="Enter Editmode") # need empty undo step
+ # XXX
+
if obj_act and obj_act.mode == 'EDIT' and obj_act.type == obj_new.type:
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
@@ -92,6 +105,7 @@ def object_data_add(context, obdata, operator=None):
#scene.objects.active = obj_new
bpy.ops.object.join() # join into the active.
+ bpy.data.meshes.remove(obdata)
bpy.ops.object.mode_set(mode='EDIT')
else:
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
new file mode 100644
index 00000000000..3877f711b7f
--- /dev/null
+++ b/release/scripts/modules/addon_utils.py
@@ -0,0 +1,326 @@
+# ##### 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__ = (
+ "paths",
+ "modules",
+ "check",
+ "enable",
+ "disable",
+ "reset_all",
+ "module_bl_info",
+)
+
+import bpy as _bpy
+
+
+def paths():
+ # RELEASE SCRIPTS: official scripts distributed in Blender releases
+ paths = _bpy.utils.script_paths("addons")
+
+ # CONTRIB SCRIPTS: good for testing but not official scripts yet
+ # if folder addons_contrib/ exists, scripts in there will be loaded too
+ paths += _bpy.utils.script_paths("addons_contrib")
+
+ # EXTERN SCRIPTS: external projects scripts
+ # if folder addons_extern/ exists, scripts in there will be loaded too
+ paths += _bpy.utils.script_paths("addons_extern")
+
+ return paths
+
+
+def modules(module_cache):
+ import os
+ import sys
+ import time
+
+ path_list = paths()
+
+ # fake module importing
+ def fake_module(mod_name, mod_path, speedy=True):
+ if _bpy.app.debug:
+ print("fake_module", mod_path, mod_name)
+ import ast
+ ModuleType = type(ast)
+ file_mod = open(mod_path, "r", encoding='UTF-8')
+ if speedy:
+ lines = []
+ line_iter = iter(file_mod)
+ l = ""
+ while not l.startswith("bl_info"):
+ l = line_iter.readline()
+ if len(l) == 0:
+ break
+ while l.rstrip():
+ lines.append(l)
+ l = line_iter.readline()
+ data = "".join(lines)
+
+ else:
+ data = file_mod.read()
+
+ file_mod.close()
+
+ try:
+ ast_data = ast.parse(data, filename=mod_path)
+ except:
+ print("Syntax error 'ast.parse' can't read %r" % mod_path)
+ import traceback
+ traceback.print_exc()
+ ast_data = None
+
+ body_info = None
+
+ if ast_data:
+ for body in ast_data.body:
+ if body.__class__ == ast.Assign:
+ if len(body.targets) == 1:
+ if getattr(body.targets[0], "id", "") == "bl_info":
+ body_info = body
+ break
+
+ if body_info:
+ try:
+ mod = ModuleType(mod_name)
+ mod.bl_info = ast.literal_eval(body.value)
+ mod.__file__ = mod_path
+ mod.__time__ = os.path.getmtime(mod_path)
+ except:
+ print("AST error in module %s" % mod_name)
+ import traceback
+ traceback.print_exc()
+ raise
+
+ return mod
+ else:
+ return None
+
+ modules_stale = set(module_cache.keys())
+
+ for path in path_list:
+ for mod_name, mod_path in _bpy.path.module_names(path):
+ modules_stale -= {mod_name}
+ mod = module_cache.get(mod_name)
+ if mod:
+ if 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
+
+ if mod is None:
+ mod = fake_module(mod_name, mod_path)
+ if mod:
+ module_cache[mod_name] = mod
+
+ # just incase we get stale modules, not likely
+ for mod_stale in modules_stale:
+ del module_cache[mod_stale]
+ del modules_stale
+
+ mod_list = list(module_cache.values())
+ mod_list.sort(key=lambda mod: (mod.bl_info['category'], mod.bl_info['name']))
+ return mod_list
+
+
+def check(module_name):
+ """
+ Returns the loaded state of the addon.
+
+ :arg module_name: The name of the addon and module.
+ :type module_name: string
+ :return: (loaded_default, loaded_state)
+ :rtype: tuple of booleans
+ """
+ import sys
+ loaded_default = module_name in _bpy.context.user_preferences.addons
+
+ mod = sys.modules.get(module_name)
+ loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis)
+
+ if loaded_state is Ellipsis:
+ print("Warning: addon-module %r found module but without"
+ " __addon_enabled__ field, possible name collision from file: %r" %
+ (module_name, getattr(mod, "__file__", "<unknown>")))
+
+ loaded_state = False
+
+ return loaded_default, loaded_state
+
+
+def enable(module_name, default_set=True):
+ """
+ Enables an addon by name.
+
+ :arg module_name: The name of the addon and module.
+ :type module_name: string
+ :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():
+ import traceback
+ traceback.print_exc()
+
+ # reload if the mtime changes
+ mod = sys.modules.get(module_name)
+ if mod:
+ mod.__addon_enabled__ = False
+ mtime_orig = getattr(mod, "__time__", 0)
+ mtime_new = os.path.getmtime(mod.__file__)
+ if mtime_orig != mtime_new:
+ print("module changed on disk:", mod.__file__, "reloading...")
+
+ try:
+ imp.reload(mod)
+ except:
+ handle_error()
+ del sys.modules[module_name]
+ return None
+ mod.__addon_enabled__ = False
+
+ # Split registering up into 3 steps so we can undo if it fails par way through
+ # 1) try import
+ try:
+ mod = __import__(module_name)
+ mod.__time__ = os.path.getmtime(mod.__file__)
+ mod.__addon_enabled__ = False
+ except:
+ handle_error()
+ return None
+
+ # 2) try register collected modules
+ # removed, addons need to handle own registration now.
+
+ # 3) try run the modules register function
+ try:
+ mod.register()
+ except:
+ handle_error()
+ del sys.modules[module_name]
+ return None
+
+ # * OK loaded successfully! *
+ if default_set:
+ # just incase its enabled alredy
+ ext = _bpy.context.user_preferences.addons.get(module_name)
+ if not ext:
+ ext = _bpy.context.user_preferences.addons.new()
+ ext.module = module_name
+
+ mod.__addon_enabled__ = True
+
+ if _bpy.app.debug:
+ print("\taddon_utils.enable", mod.__name__)
+
+ return mod
+
+
+def disable(module_name, default_set=True):
+ """
+ Disables an addon by name.
+
+ :arg module_name: The name of the addon and module.
+ :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.
+ # so even if the module is not found, still disable the addon in the user prefs.
+ if mod:
+ mod.__addon_enabled__ = False
+
+ try:
+ mod.unregister()
+ except:
+ import traceback
+ traceback.print_exc()
+ else:
+ print("addon_utils.disable", module_name, "not loaded")
+
+ # could be in more then once, unlikely but better do this just incase.
+ addons = _bpy.context.user_preferences.addons
+
+ if default_set:
+ while module_name in addons:
+ addon = addons.get(module_name)
+ if addon:
+ addons.remove(addon)
+
+ if _bpy.app.debug:
+ print("\taddon_utils.disable", module_name)
+
+
+def reset_all(reload_scripts=False):
+ """
+ Sets the addon state based on the user preferences.
+ """
+ import sys
+ import imp
+
+ # RELEASE SCRIPTS: official scripts distributed in Blender releases
+ paths_list = paths()
+
+ for path in paths_list:
+ _bpy.utils._sys_path_ensure(path)
+ for mod_name, mod_path in _bpy.path.module_names(path):
+ is_enabled, is_loaded = check(mod_name)
+
+ # first check if reload is needed before changing state.
+ if reload_scripts:
+ mod = sys.modules.get(mod_name)
+ if mod:
+ imp.reload(mod)
+
+ if is_enabled == is_loaded:
+ pass
+ elif is_enabled:
+ enable(mod_name)
+ elif is_loaded:
+ print("\taddon_utils.reset_all unloading", mod_name)
+ disable(mod_name)
+
+
+def module_bl_info(mod, info_basis={"name": "", "author": "", "version": (), "blender": (), "api": 0, "location": "", "description": "", "wiki_url": "", "tracker_url": "", "support": 'COMMUNITY', "category": "", "warning": "", "show_expanded": False}):
+ addon_info = getattr(mod, "bl_info", {})
+
+ # avoid re-initializing
+ if "_init" in addon_info:
+ return addon_info
+
+ if not addon_info:
+ mod.bl_info = addon_info
+
+ for key, value in info_basis.items():
+ addon_info.setdefault(key, value)
+
+ if not addon_info["name"]:
+ addon_info["name"] = mod.__name__
+
+ addon_info["_init"] = None
+ return addon_info
diff --git a/release/scripts/modules/animsys_refactor.py b/release/scripts/modules/animsys_refactor.py
index 464df870e87..5336a8b2b35 100644
--- a/release/scripts/modules/animsys_refactor.py
+++ b/release/scripts/modules/animsys_refactor.py
@@ -28,6 +28,12 @@ The main function to use is: update_data_paths(...)
IS_TESTING = False
+def drepr(string):
+ # is there a less crappy way to do this in python?, re.escape also escapes
+ # single quotes strings so cant use it.
+ return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'")
+
+
class DataPathBuilder(object):
__slots__ = ("data_path", )
""" Dummy class used to parse fcurve and driver data paths.
@@ -40,7 +46,12 @@ class DataPathBuilder(object):
return DataPathBuilder(self.data_path + (str_value, ))
def __getitem__(self, key):
- str_value = '["%s"]' % key
+ if type(key) is int:
+ str_value = '[%d]' % key
+ elif type(key) is str:
+ str_value = '[%s]' % drepr(key)
+ else:
+ raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key)))
return DataPathBuilder(self.data_path + (str_value, ))
def resolve(self, real_base, rna_update_from_map=None):
@@ -170,6 +181,15 @@ def update_data_paths(rna_update):
continue
for fcurve in anim_data.drivers:
+ data_path = fcurve.data_path
+ data_path_new = find_path_new(anim_data_base, data_path, rna_update_dict, rna_update_from_map)
+ # print(data_path_new)
+ if data_path_new != data_path:
+ if not IS_TESTING:
+ fcurve.data_path = data_path_new
+ fcurve.driver.is_valid = True # reset to allow this to work again
+ print("driver-fcurve (%s): %s -> %s" % (id_data.name, data_path, data_path_new))
+
for var in fcurve.driver.variables:
if var.type == 'SINGLE_PROP':
for tar in var.targets:
diff --git a/release/scripts/modules/bpy/__init__.py b/release/scripts/modules/bpy/__init__.py
index 5c636d3a0df..1df8e9e5588 100644
--- a/release/scripts/modules/bpy/__init__.py
+++ b/release/scripts/modules/bpy/__init__.py
@@ -18,6 +18,10 @@
# <pep8 compliant>
+"""
+Give access to blender data and utility functions.
+"""
+
# internal blender C module
import _bpy
from _bpy import types, props, app
@@ -37,18 +41,6 @@ import sys as _sys
def _main():
- ## security issue, dont allow the $CWD in the path.
- ## note: this removes "" but not "." which are the same, security
- ## people need to explain how this is even a fix.
- # _sys.path[:] = filter(None, _sys.path)
-
- # because of how the console works. we need our own help() pager func.
- # replace the bold function because it adds crazy chars
- import pydoc
- pydoc.getpager = lambda: pydoc.plainpager
- pydoc.Helper.getline = lambda self, prompt: None
- pydoc.TextDoc.use_bold = lambda self, text: text
-
# Possibly temp. addons path
from os.path import join, dirname, normpath
_sys.path.append(normpath(join(dirname(__file__), "..", "..", "addons", "modules")))
diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py
index 9a29b713882..f7e5b988cc8 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -144,6 +144,9 @@ def resolve_ncase(path):
dirpath = os.path.dirname(dirpath)
if not os.path.exists(dirpath):
+ if dirpath == path:
+ return path, False
+
dirpath, found = _ncase_path_found(dirpath)
if not found:
diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py
index e3e93203235..a2d7b9e502f 100644
--- a/release/scripts/modules/bpy/utils.py
+++ b/release/scripts/modules/bpy/utils.py
@@ -23,10 +23,7 @@ This module contains utility functions specific to blender but
not assosiated with blenders internal data.
"""
-from _bpy import register_class
-from _bpy import unregister_class
-
-from _bpy import blend_paths
+from _bpy import register_class, unregister_class, blend_paths, resource_path
from _bpy import script_paths as _bpy_script_paths
from _bpy import user_resource as _user_resource
@@ -34,24 +31,32 @@ import bpy as _bpy
import os as _os
import sys as _sys
+import addon_utils as _addon_utils
+
+_script_module_dirs = "startup", "modules"
+
def _test_import(module_name, loaded_modules):
- import traceback
- import time
+ use_time = _bpy.app.debug
+
if module_name in loaded_modules:
return None
if "." in module_name:
print("Ignoring '%s', can't import files containing multiple periods." % module_name)
return None
- t = time.time()
+ if use_time:
+ import time
+ t = time.time()
+
try:
mod = __import__(module_name)
except:
+ import traceback
traceback.print_exc()
return None
- if _bpy.app.debug:
+ if use_time:
print("time %s %.4f" % (module_name, time.time() - t))
loaded_modules.add(mod.__name__) # should match mod.__name__ too
@@ -74,9 +79,6 @@ def modules_from_path(path, loaded_modules):
:return: all loaded modules.
:rtype: list
"""
- import traceback
- import time
-
modules = []
for mod_name, mod_path in _bpy.path.module_names(path):
@@ -100,10 +102,11 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
:arg refresh_scripts: only load scripts which are not already loaded as modules.
:type refresh_scripts: bool
"""
- import traceback
- import time
+ use_time = _bpy.app.debug
- t_main = time.time()
+ if use_time:
+ import time
+ t_main = time.time()
loaded_modules = set()
@@ -117,7 +120,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
# note that they will only actually reload of the modification time changes.
# this `wont` work for packages so... its not perfect.
for module_name in [ext.module for ext in _bpy.context.user_preferences.addons]:
- addon_disable(module_name, default_set=False)
+ _addon_utils.disable(module_name, default_set=False)
def register_module_call(mod):
register = getattr(mod, "register", None)
@@ -125,6 +128,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
try:
register()
except:
+ import traceback
traceback.print_exc()
else:
print("\nWarning! '%s' has no register function, this is now a requirement for registerable scripts." % mod.__file__)
@@ -135,6 +139,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
try:
unregister()
except:
+ import traceback
traceback.print_exc()
def test_reload(mod):
@@ -148,6 +153,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
try:
return imp.reload(mod)
except:
+ import traceback
traceback.print_exc()
def test_register(mod):
@@ -178,10 +184,8 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
_global_loaded_modules[:] = []
- user_path = user_script_path()
-
for base_path in script_paths():
- for path_subdir in ("", "ui", "op", "io", "keyingsets", "modules"):
+ for path_subdir in _script_module_dirs:
path = _os.path.join(base_path, path_subdir)
if _os.path.isdir(path):
_sys_path_ensure(path)
@@ -190,14 +194,11 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
if path_subdir == "modules":
continue
- if user_path != base_path and path_subdir == "":
- continue # avoid loading 2.4x scripts
-
for mod in modules_from_path(path, loaded_modules):
test_register(mod)
- # deal with addons seperately
- addon_reset_all(reload_scripts)
+ # deal with addons separately
+ _addon_utils.reset_all(reload_scripts)
# run the active integration preset
filepath = preset_find(_bpy.context.user_preferences.inputs.active_keyconfig, "keyconfig")
@@ -208,7 +209,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
import gc
print("gc.collect() -> %d" % gc.collect())
- if _bpy.app.debug:
+ if use_time:
print("Python Script Load Time %.4f" % (time.time() - t_main))
@@ -227,27 +228,41 @@ def user_script_path():
return None
-def script_paths(subdir=None, user=True):
+def script_paths(subdir=None, user_pref=True, all=False):
"""
- Returns a list of valid script paths from the home directory and user preferences.
+ Returns a list of valid script paths.
- Accepts any number of string arguments which are joined to make a path.
+ :arg subdir: Optional subdir.
+ :type subdir: string
+ :arg user_pref: Include the user preference script path.
+ :type user_pref: bool
+ :arg all: Include local, user and system paths rather just the paths blender uses.
+ :type all: bool
+ :return: script paths.
+ :rtype: list
"""
scripts = list(_scripts)
# add user scripts dir
- if user:
+ if user_pref:
user_script_path = _bpy.context.user_preferences.filepaths.script_directory
else:
user_script_path = None
- for path in _bpy_script_paths() + (user_script_path, ):
+ if all:
+ # all possible paths
+ base_paths = tuple(_os.path.join(resource_path(res), "scripts") for res in ('LOCAL', 'USER', 'SYSTEM'))
+ else:
+ # only paths blender uses
+ base_paths = _bpy_script_paths()
+
+ for path in base_paths + (user_script_path, ):
if path:
path = _os.path.normpath(path)
if path not in scripts and _os.path.isdir(path):
scripts.append(path)
- if not subdir:
+ if subdir is None:
return scripts
script_paths = []
@@ -259,6 +274,24 @@ def script_paths(subdir=None, user=True):
return script_paths
+def refresh_script_paths():
+ """
+ Run this after creating new script paths to update sys.path
+ """
+
+ for base_path in script_paths():
+ for path_subdir in _script_module_dirs:
+ path = _os.path.join(base_path, path_subdir)
+ if _os.path.isdir(path):
+ _sys_path_ensure(path)
+
+ for path in _addon_utils.paths():
+ _sys_path_ensure(path)
+ path = _os.path.join(path, "modules")
+ if _os.path.isdir(path):
+ _sys_path_ensure(path)
+
+
_presets = _os.path.join(_scripts[0], "presets") # FIXME - multiple paths
@@ -267,7 +300,7 @@ def preset_paths(subdir):
Returns a list of paths for a specific preset.
"""
dirs = []
- for path in script_paths("presets"):
+ for path in script_paths("presets", all=True):
directory = _os.path.join(path, subdir)
if _os.path.isdir(directory):
dirs.append(directory)
@@ -322,176 +355,6 @@ def smpte_from_frame(frame, fps=None, fps_base=None):
return smpte_from_seconds((frame * fps_base) / fps, fps)
-def addon_check(module_name):
- """
- Returns the loaded state of the addon.
-
- :arg module_name: The name of the addon and module.
- :type module_name: string
- :return: (loaded_default, loaded_state)
- :rtype: tuple of booleans
- """
- loaded_default = module_name in _bpy.context.user_preferences.addons
-
- mod = _sys.modules.get(module_name)
- loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis)
-
- if loaded_state is Ellipsis:
- print("Warning: addon-module %r found module but without"
- " __addon_enabled__ field, possible name collision from file: %r" %
- (module_name, getattr(mod, "__file__", "<unknown>")))
-
- loaded_state = False
-
- return loaded_default, loaded_state
-
-
-def addon_enable(module_name, default_set=True):
- """
- Enables an addon by name.
-
- :arg module_name: The name of the addon and module.
- :type module_name: string
- :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():
- import traceback
- traceback.print_exc()
-
- # reload if the mtime changes
- mod = sys.modules.get(module_name)
- if mod:
- mod.__addon_enabled__ = False
- mtime_orig = getattr(mod, "__time__", 0)
- mtime_new = os.path.getmtime(mod.__file__)
- if mtime_orig != mtime_new:
- print("module changed on disk:", mod.__file__, "reloading...")
-
- try:
- imp.reload(mod)
- except:
- handle_error()
- del sys.modules[module_name]
- return None
- mod.__addon_enabled__ = False
-
- # Split registering up into 3 steps so we can undo if it fails par way through
- # 1) try import
- try:
- mod = __import__(module_name)
- mod.__time__ = os.path.getmtime(mod.__file__)
- mod.__addon_enabled__ = False
- except:
- handle_error()
- return None
-
- # 2) try register collected modules
- # removed, addons need to handle own registration now.
-
- # 3) try run the modules register function
- try:
- mod.register()
- except:
- handle_error()
- del sys.modules[module_name]
- return None
-
- # * OK loaded successfully! *
- if default_set:
- # just incase its enabled alredy
- ext = _bpy.context.user_preferences.addons.get(module_name)
- if not ext:
- ext = _bpy.context.user_preferences.addons.new()
- ext.module = module_name
-
- mod.__addon_enabled__ = True
-
- if _bpy.app.debug:
- print("\tbpy.utils.addon_enable", mod.__name__)
-
- return mod
-
-
-def addon_disable(module_name, default_set=True):
- """
- Disables an addon by name.
-
- :arg module_name: The name of the addon and module.
- :type module_name: string
- """
- import traceback
- 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.
- # so even if the module is not found, still disable the addon in the user prefs.
- if mod:
- mod.__addon_enabled__ = False
-
- try:
- mod.unregister()
- except:
- traceback.print_exc()
- else:
- print("addon_disable", module_name, "not loaded")
-
- # could be in more then once, unlikely but better do this just incase.
- addons = _bpy.context.user_preferences.addons
-
- if default_set:
- while module_name in addons:
- addon = addons.get(module_name)
- if addon:
- addons.remove(addon)
-
- if _bpy.app.debug:
- print("\tbpy.utils.addon_disable", module_name)
-
-
-def addon_reset_all(reload_scripts=False):
- """
- Sets the addon state based on the user preferences.
- """
- import imp
-
- # RELEASE SCRIPTS: official scripts distributed in Blender releases
- paths = script_paths("addons")
-
- # CONTRIB SCRIPTS: good for testing but not official scripts yet
- paths += script_paths("addons_contrib")
-
- # EXTERN SCRIPTS: external projects scripts
- paths += script_paths("addons_extern")
-
- for path in paths:
- _sys_path_ensure(path)
- for mod_name, mod_path in _bpy.path.module_names(path):
- is_enabled, is_loaded = addon_check(mod_name)
-
- # first check if reload is needed before changing state.
- if reload_scripts:
- mod = _sys.modules.get(mod_name)
- if mod:
- imp.reload(mod)
-
- if is_enabled == is_loaded:
- pass
- elif is_enabled:
- addon_enable(mod_name)
- elif is_loaded:
- print("\taddon_reset_all unloading", mod_name)
- addon_disable(mod_name)
-
-
def preset_find(name, preset_path, display_name=False):
if not name:
return None
@@ -518,12 +381,13 @@ def keyconfig_set(filepath):
print("loading preset:", filepath)
keyconfigs = _bpy.context.window_manager.keyconfigs
- kc_orig = keyconfigs.active
keyconfigs_old = keyconfigs[:]
try:
- exec(compile(open(filepath).read(), filepath, 'exec'), {"__file__": filepath})
+ file = open(filepath)
+ exec(compile(file.read(), filepath, 'exec'), {"__file__": filepath})
+ file.close()
except:
import traceback
traceback.print_exc()
@@ -583,48 +447,47 @@ def _bpy_module_classes(module, is_registered=False):
typemap_list = _bpy_types.TypeMap.get(module, ())
i = 0
while i < len(typemap_list):
- cls_weakref, path, line = typemap_list[i]
+ cls_weakref = typemap_list[i]
cls = cls_weakref()
if cls is None:
del typemap_list[i]
else:
- if is_registered == ("bl_rna" in cls.__dict__):
- yield (cls, path, line)
+ if is_registered == cls.is_registered:
+ yield cls
i += 1
def register_module(module, verbose=False):
- import traceback
if verbose:
print("bpy.utils.register_module(%r): ..." % module)
- for cls, path, line in _bpy_module_classes(module, is_registered=False):
+ cls = None
+ for cls in _bpy_module_classes(module, is_registered=False):
if verbose:
- print(" %s of %s:%s" % (cls, path, line))
+ print(" %r" % cls)
try:
register_class(cls)
except:
- print("bpy.utils.register_module(): failed to registering class '%s.%s'" % (cls.__module__, cls.__name__))
- print("\t", path, "line", line)
+ print("bpy.utils.register_module(): failed to registering class %r" % cls)
+ import traceback
traceback.print_exc()
if verbose:
print("done.\n")
- if "cls" not in locals():
+ if cls is None:
raise Exception("register_module(%r): defines no classes" % module)
def unregister_module(module, verbose=False):
- import traceback
if verbose:
print("bpy.utils.unregister_module(%r): ..." % module)
- for cls, path, line in _bpy_module_classes(module, is_registered=True):
+ for cls in _bpy_module_classes(module, is_registered=True):
if verbose:
- print(" %s of %s:%s" % (cls, path, line))
+ print(" %r" % cls)
try:
unregister_class(cls)
except:
- print("bpy.utils.unregister_module(): failed to unregistering class '%s.%s'" % (cls.__module__, cls.__name__))
- print("\t", path, "line", line)
+ print("bpy.utils.unregister_module(): failed to unregistering class %r" % cls)
+ import traceback
traceback.print_exc()
if verbose:
- print("done.\n") \ No newline at end of file
+ print("done.\n")
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index 4eb712a65cc..c3352dd33ad 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -23,9 +23,11 @@ import _bpy
from mathutils import Vector
StructRNA = bpy_types.Struct.__bases__[0]
-StructMetaIDProp = _bpy.StructMetaIDProp
+StructMetaPropGroup = _bpy.StructMetaPropGroup
# StructRNA = bpy_types.Struct
+bpy_types.BlendDataLibraries.load = _bpy._library_load
+
class Context(StructRNA):
__slots__ = ()
@@ -188,7 +190,7 @@ class _GenericBone:
@length.setter
def length(self, value):
- self.tail = self.head + ((self.tail - self.head).normalize() * value)
+ self.tail = self.head + ((self.tail - self.head).normalized() * value)
@property
def vector(self):
@@ -258,15 +260,15 @@ class _GenericBone:
return bones
-class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaIDProp):
+class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
__slots__ = ()
-class Bone(StructRNA, _GenericBone, metaclass=StructMetaIDProp):
+class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
__slots__ = ()
-class EditBone(StructRNA, _GenericBone, metaclass=StructMetaIDProp):
+class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
__slots__ = ()
def align_orientation(self, other):
@@ -278,19 +280,29 @@ class EditBone(StructRNA, _GenericBone, metaclass=StructMetaIDProp):
self.tail = self.head + vec
self.roll = other.roll
- def transform(self, matrix):
+ def transform(self, matrix, scale=True, roll=True):
"""
Transform the the bones head, tail, roll and envalope (when the matrix has a scale component).
- Expects a 4x4 or 3x3 matrix.
+
+ :arg matrix: 3x3 or 4x4 transformation matrix.
+ :type matrix: :class:`Matrix`
+ :arg scale: Scale the bone envalope by the matrix.
+ :type scale: bool
+ :arg roll: Correct the roll to point in the same relative direction to the head and tail.
+ :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
- scalar = matrix.median_scale
- self.head_radius *= scalar
- self.tail_radius *= scalar
- self.align_roll(z_vec * matrix)
+
+ if scale:
+ scalar = matrix.median_scale
+ self.head_radius *= scalar
+ self.tail_radius *= scalar
+
+ if roll:
+ self.align_roll(z_vec * matrix)
def ord_ind(i1, i2):
@@ -557,37 +569,49 @@ TypeMap = {}
class RNAMeta(type):
def __new__(cls, name, bases, classdict, **args):
result = type.__new__(cls, name, bases, classdict)
- if bases and bases[0] != StructRNA:
- import traceback
- import weakref
+ if bases and bases[0] is not StructRNA:
+ from _weakref import ref as ref
module = result.__module__
# first part of packages only
if "." in module:
module = module[:module.index(".")]
- sf = traceback.extract_stack(limit=2)[0]
-
- TypeMap.setdefault(module, []).append((weakref.ref(result), sf[0], sf[1]))
+ TypeMap.setdefault(module, []).append(ref(result))
return result
+ @property
+ def is_registered(cls):
+ return "bl_rna" in cls.__dict__
+
+
+class OrderedDictMini(dict):
+ def __init__(self, *args):
+ self.order = []
+ dict.__init__(self, args)
+
+ def __setitem__(self, key, val):
+ dict.__setitem__(self, key, val)
+ if key not in self.order:
+ self.order.append(key)
-import collections
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self.order.remove(key)
-class RNAMetaIDProp(RNAMeta, StructMetaIDProp):
+class RNAMetaPropGroup(RNAMeta, StructMetaPropGroup):
pass
class OrderedMeta(RNAMeta):
-
def __init__(cls, name, bases, attributes):
- super(OrderedMeta, cls).__init__(name, bases, attributes)
- cls.order = list(attributes.keys())
+ if attributes.__class__ is OrderedDictMini:
+ cls.order = attributes.order
def __prepare__(name, bases, **kwargs):
- return collections.OrderedDict()
+ return OrderedDictMini() # collections.OrderedDict()
# Only defined so operators members can be used by accessing self.order
@@ -634,7 +658,7 @@ class Macro(StructRNA, metaclass=OrderedMeta):
return ops.macro_define(self, opname)
-class IDPropertyGroup(StructRNA, metaclass=RNAMetaIDProp):
+class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
__slots__ = ()
@@ -656,6 +680,9 @@ class _GenericUI:
if draw_funcs is None:
def draw_ls(self, context):
+ # ensure menus always get default context
+ operator_context_default = self.layout.operator_context
+
for func in draw_ls._draw_funcs:
# so bad menu functions dont stop the entire menu from drawing.
try:
@@ -664,6 +691,8 @@ class _GenericUI:
import traceback
traceback.print_exc()
+ self.layout.operator_context = operator_context_default
+
draw_funcs = draw_ls._draw_funcs = [cls.draw]
cls.draw = draw_ls
@@ -671,7 +700,7 @@ class _GenericUI:
@classmethod
def append(cls, draw_func):
- """Prepend an draw function to this menu, takes the same arguments as the menus draw function."""
+ """Append a draw function to this menu, takes the same arguments as the menus draw function."""
draw_funcs = cls._dyn_ui_initialize()
draw_funcs.append(draw_func)
diff --git a/release/scripts/modules/bpyml.py b/release/scripts/modules/bpyml.py
index f1f72d50fd2..fdf5172a0b3 100644
--- a/release/scripts/modules/bpyml.py
+++ b/release/scripts/modules/bpyml.py
@@ -160,9 +160,9 @@ if __name__ == "__main__":
from bpyml_test import *
draw = [
- ui() [
- split() [
- column() [
+ ui()[
+ split()[
+ column()[
prop(data='context.scene.render', property='use_stamp_time', text='Time'),
prop(data='context.scene.render', property='use_stamp_date', text='Date'),
prop(data='context.scene.render', property='use_stamp_render_time', text='RenderTime'),
@@ -173,7 +173,7 @@ if __name__ == "__main__":
prop(data='context.scene.render', property='use_stamp_marker', text='Marker'),
prop(data='context.scene.render', property='use_stamp_sequencer_strip', text='Seq. Strip')
],
- column() [
+ column()[
active(expr='context.scene.render.use_stamp'),
prop(data='context.scene.render', property='stamp_foreground', slider=True),
prop(data='context.scene.render', property='stamp_background', slider=True),
@@ -181,9 +181,9 @@ if __name__ == "__main__":
prop(data='context.scene.render', property='stamp_font_size', text='Font Size')
]
],
- split(percentage=0.2) [
+ split(percentage=0.2)[
prop(data='context.scene.render', property='use_stamp_note', text='Note'),
- row() [
+ row()[
active(expr='context.scene.render.use_stamp_note'),
prop(data='context.scene.render', property='stamp_note_text', text='')
]
diff --git a/release/scripts/modules/bpyml_ui.py b/release/scripts/modules/bpyml_ui.py
index 2462dd60e3e..1e0522974d1 100644
--- a/release/scripts/modules/bpyml_ui.py
+++ b/release/scripts/modules/bpyml_ui.py
@@ -65,7 +65,7 @@ def _parse_rna_args(base, py_node):
def _call_recursive(context, base, py_node):
- prop = base.bl_rna.properties.get(py_node[TAG])
+ # prop = base.bl_rna.properties.get(py_node[TAG])
if py_node[TAG] in base.bl_rna.properties:
value = py_node[ARGS].get("expr")
if value:
@@ -73,7 +73,7 @@ def _call_recursive(context, base, py_node):
setattr(base, py_node[TAG], value)
else:
value = py_node[ARGS]['value'] # have to have this
- setattr(base, name, value)
+ setattr(base, py_node[TAG], value)
else:
args = _parse_rna_args(base, py_node)
func_new = getattr(base, py_node[TAG])
diff --git a/release/scripts/op/console_python.py b/release/scripts/modules/console_python.py
index 98d3f3f74a3..3048fa1d597 100644
--- a/release/scripts/op/console_python.py
+++ b/release/scripts/modules/console_python.py
@@ -33,6 +33,20 @@ def add_scrollback(text, text_type):
type=text_type)
+def replace_help(namespace):
+ def _help(*args):
+ # because of how the console works. we need our own help() pager func.
+ # replace the bold function because it adds crazy chars
+ import pydoc
+ pydoc.getpager = lambda: pydoc.plainpager
+ pydoc.Helper.getline = lambda self, prompt: None
+ pydoc.TextDoc.use_bold = lambda self, text: text
+
+ pydoc.help(*args)
+
+ namespace["help"] = _help
+
+
def get_console(console_id):
'''
helper function for console operators
@@ -83,11 +97,13 @@ def get_console(console_id):
namespace["bpy"] = bpy
namespace["C"] = bpy.context
- namespace.update(__import__("mathutils").__dict__) # from mathutils import *
- namespace.update(__import__("math").__dict__) # from math import *
+ replace_help(namespace)
console = InteractiveConsole(locals=namespace, filename="<blender_console>")
+ console.push("from mathutils import *")
+ console.push("from math import *")
+
if _BPY_MAIN_OWN:
console._bpy_main_mod = bpy_main_mod
@@ -224,12 +240,20 @@ def autocomplete(context):
# This function isnt aware of the text editor or being an operator
# just does the autocomp then copy its results back
- current_line.body, current_line.current_character, scrollback = \
- intellisense.expand(
- line=current_line.body,
+ result = intellisense.expand(
+ line=line,
cursor=current_line.current_character,
namespace=console.locals,
private=bpy.app.debug)
+
+ line_new = result[0]
+ current_line.body, current_line.current_character, scrollback = result
+ del result
+
+ # update sel. setting body should really do this!
+ ofs = len(line_new) - len(line)
+ sc.select_start += ofs
+ sc.select_end += ofs
except:
# unlikely, but this can happen with unicode errors for example.
# or if the api attribute access its self causes an error.
@@ -272,23 +296,11 @@ def banner(context):
add_scrollback("Execute: Enter", 'OUTPUT')
add_scrollback("Autocomplete: Ctrl+Space", 'OUTPUT')
add_scrollback("Ctrl +/- Wheel: Zoom", 'OUTPUT')
- add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, bgl, blf, mathutils", 'OUTPUT')
+ add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, bpy.utils, bgl, blf, mathutils", 'OUTPUT')
add_scrollback("Convenience Imports: from mathutils import *; from math import *", 'OUTPUT')
add_scrollback("", 'OUTPUT')
- add_scrollback(" WARNING!!! Blender 2.5 API is subject to change, see API reference for more info.", 'ERROR')
- add_scrollback("", 'OUTPUT')
+ # add_scrollback(" WARNING!!! Blender 2.5 API is subject to change, see API reference for more info.", 'ERROR')
+ # add_scrollback("", 'OUTPUT')
sc.prompt = PROMPT
return {'FINISHED'}
-
-
-def register():
- pass
-
-
-def unregister():
- pass
-
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/console_shell.py b/release/scripts/modules/console_shell.py
index 2c5b48acd34..7a6f45c426f 100644
--- a/release/scripts/op/console_shell.py
+++ b/release/scripts/modules/console_shell.py
@@ -76,14 +76,3 @@ def banner(context):
sc.prompt = os.getcwd() + PROMPT
return {'FINISHED'}
-
-
-def register():
- pass
-
-
-def unregister():
- pass
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/modules/image_utils.py b/release/scripts/modules/image_utils.py
index d74c89ac173..39e49ee1f96 100644
--- a/release/scripts/modules/image_utils.py
+++ b/release/scripts/modules/image_utils.py
@@ -23,5 +23,5 @@ def image_load(filepath, dirpath, place_holder=False, recursive=False, convert_c
import bpy
try:
return bpy.data.images.load(filepath)
- except SystemError:
+ except RuntimeError:
return bpy.data.images.new("Untitled", 128, 128)
diff --git a/release/scripts/modules/io_utils.py b/release/scripts/modules/io_utils.py
index 39b38669188..820d7cfa39d 100644
--- a/release/scripts/modules/io_utils.py
+++ b/release/scripts/modules/io_utils.py
@@ -19,13 +19,17 @@
# <pep8 compliant>
import bpy
-from bpy.props import *
+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:
@@ -41,12 +45,18 @@ class ExportHelper:
return {'RUNNING_MODAL'}
def check(self, context):
- filepath = bpy.path.ensure_ext(self.filepath, self.filename_ext)
+ 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
- else:
- return False
+
+ return False
class ImportHelper:
@@ -57,6 +67,86 @@ class ImportHelper:
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 = (
+ {0x5c, 0x9a, 0x119, 0x15d, 0x20b, 0x2a2, 0x2c8, 0x365, 0x413, 0x46c, 0x4d0, 0x529, 0x644, 0x682, 0x701, 0x745, 0x823, 0x88a, 0x8e0, 0x94d, 0xa2b, 0xa54, 0xae8, 0xb11},
+ {0x9c, 0xac, 0x159, 0x169, 0x22b, 0x2e8, 0x40b, 0x465, 0x4c8, 0x522, 0x684, 0x694, 0x741, 0x751, 0x813, 0x8d0, 0xa23, 0xa4d, 0xae0, 0xb0a},
+ {0x99, 0xa9, 0x15c, 0x16c, 0x213, 0x2d0, 0x423, 0x44a, 0x4e0, 0x50d, 0x681, 0x691, 0x744, 0x754, 0x82b, 0x8e8, 0xa0b, 0xa62, 0xac8, 0xb25},
+ {0x59, 0x85, 0x11c, 0x142, 0x223, 0x28d, 0x2e0, 0x34a, 0x42b, 0x469, 0x4e8, 0x52c, 0x641, 0x69d, 0x704, 0x75a, 0x80b, 0x8a5, 0x8c8, 0x962, 0xa13, 0xa51, 0xad0, 0xb14},
+ {0xa5, 0x162, 0x21c, 0x285, 0x2d9, 0x342, 0x463, 0x46b, 0x520, 0x528, 0x68d, 0x74a, 0x804, 0x89d, 0x8c1, 0x95a, 0xa4b, 0xa53, 0xb08, 0xb10},
+ {0x4b, 0x53, 0x108, 0x110, 0x29c, 0x2ac, 0x359, 0x369, 0x41a, 0x422, 0x4dd, 0x4e5, 0x663, 0x66b, 0x720, 0x728, 0x884, 0x894, 0x941, 0x951, 0xa02, 0xa0a, 0xac5, 0xacd},
+ {0x63, 0x6b, 0x120, 0x128, 0x299, 0x2a9, 0x35c, 0x36c, 0x405, 0x40d, 0x4c2, 0x4ca, 0x64b, 0x653, 0x708, 0x710, 0x881, 0x891, 0x944, 0x954, 0xa1d, 0xa25, 0xada, 0xae2},
+ {0x8a, 0x14d, 0x219, 0x29a, 0x2dc, 0x35d, 0x44b, 0x453, 0x508, 0x510, 0x6a2, 0x765, 0x801, 0x882, 0x8c4, 0x945, 0xa63, 0xa6b, 0xb20, 0xb28},
+ {0x5a, 0x62, 0x8b, 0x11d, 0x125, 0x148, 0x22c, 0x28b, 0x293, 0x2e9, 0x348, 0x350, 0x41c, 0x42c, 0x45a, 0x4d9, 0x4e9, 0x51d, 0x642, 0x64a, 0x6a3, 0x705, 0x70d, 0x760, 0x814, 0x8a3, 0x8ab, 0x8d1, 0x960, 0x968, 0xa04, 0xa14, 0xa42, 0xac1, 0xad1, 0xb05},
+ {0x54, 0xab, 0x111, 0x168, 0x21d, 0x225, 0x2da, 0x2e2, 0x45c, 0x519, 0x66c, 0x693, 0x729, 0x750, 0x805, 0x80d, 0x8c2, 0x8ca, 0xa44, 0xb01},
+ {0x51, 0x93, 0x114, 0x150, 0x202, 0x20a, 0x2c5, 0x2cd, 0x459, 0x51c, 0x669, 0x6ab, 0x72c, 0x768, 0x81a, 0x822, 0x8dd, 0x8e5, 0xa41, 0xb04},
+ {0x45, 0x4d, 0xa3, 0x102, 0x10a, 0x160, 0x229, 0x2a3, 0x2ab, 0x2ec, 0x360, 0x368, 0x419, 0x429, 0x445, 0x4dc, 0x4ec, 0x502, 0x65d, 0x665, 0x68b, 0x71a, 0x722, 0x748, 0x811, 0x88b, 0x893, 0x8d4, 0x948, 0x950, 0xa01, 0xa11, 0xa5d, 0xac4, 0xad4, 0xb1a},
+ {0x5d, 0x65, 0xa0, 0x11a, 0x122, 0x163, 0x214, 0x2a0, 0x2a8, 0x2d1, 0x363, 0x36b, 0x404, 0x414, 0x45d, 0x4c1, 0x4d1, 0x51a, 0x645, 0x64d, 0x688, 0x702, 0x70a, 0x74b, 0x82c, 0x888, 0x890, 0x8e9, 0x94b, 0x953, 0xa1c, 0xa2c, 0xa45, 0xad9, 0xae9, 0xb02},
+ {0x6c, 0x90, 0x129, 0x153, 0x21a, 0x222, 0x2dd, 0x2e5, 0x444, 0x501, 0x654, 0x6a8, 0x711, 0x76b, 0x802, 0x80a, 0x8c5, 0x8cd, 0xa5c, 0xb19},
+ {0x69, 0xa8, 0x12c, 0x16b, 0x205, 0x20d, 0x2c2, 0x2ca, 0x441, 0x504, 0x651, 0x690, 0x714, 0x753, 0x81d, 0x825, 0x8da, 0x8e2, 0xa59, 0xb1c},
+ {0x42, 0x4a, 0x88, 0x105, 0x10d, 0x14b, 0x211, 0x288, 0x290, 0x2d4, 0x34b, 0x353, 0x401, 0x411, 0x442, 0x4c4, 0x4d4, 0x505, 0x65a, 0x662, 0x6a0, 0x71d, 0x725, 0x763, 0x829, 0x8a0, 0x8a8, 0x8ec, 0x963, 0x96b, 0xa19, 0xa29, 0xa5a, 0xadc, 0xaec, 0xb1d},
+ {0xa2, 0x165, 0x204, 0x282, 0x2c1, 0x345, 0x448, 0x450, 0x50b, 0x513, 0x68a, 0x74d, 0x81c, 0x89a, 0x8d9, 0x95d, 0xa60, 0xa68, 0xb23, 0xb2b},
+ {0x60, 0x68, 0x123, 0x12b, 0x284, 0x294, 0x341, 0x351, 0x41d, 0x425, 0x4da, 0x4e2, 0x648, 0x650, 0x70b, 0x713, 0x89c, 0x8ac, 0x959, 0x969, 0xa05, 0xa0d, 0xac2, 0xaca},
+ {0x48, 0x50, 0x10b, 0x113, 0x281, 0x291, 0x344, 0x354, 0x402, 0x40a, 0x4c5, 0x4cd, 0x660, 0x668, 0x723, 0x72b, 0x899, 0x8a9, 0x95c, 0x96c, 0xa1a, 0xa22, 0xadd, 0xae5},
+ {0x8d, 0x14a, 0x201, 0x29d, 0x2c4, 0x35a, 0x460, 0x468, 0x523, 0x52b, 0x6a5, 0x762, 0x819, 0x885, 0x8dc, 0x942, 0xa48, 0xa50, 0xb0b, 0xb13},
+ {0x44, 0x9d, 0x101, 0x15a, 0x220, 0x2a5, 0x2e3, 0x362, 0x428, 0x454, 0x4eb, 0x511, 0x65c, 0x685, 0x719, 0x742, 0x808, 0x88d, 0x8cb, 0x94a, 0xa10, 0xa6c, 0xad3, 0xb29},
+ {0x84, 0x94, 0x141, 0x151, 0x210, 0x2d3, 0x420, 0x462, 0x4e3, 0x525, 0x69c, 0x6ac, 0x759, 0x769, 0x828, 0x8eb, 0xa08, 0xa4a, 0xacb, 0xb0d},
+ {0x81, 0x91, 0x144, 0x154, 0x228, 0x2eb, 0x408, 0x44d, 0x4cb, 0x50a, 0x699, 0x6a9, 0x75c, 0x76c, 0x810, 0x8d3, 0xa20, 0xa65, 0xae3, 0xb22},
+ )
+
+_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()
+
+ 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("internal error")
+
+
# limited replacement for BPyImage.comprehensiveImageLoad
def load_image(imagepath, dirname):
import os
@@ -81,14 +171,14 @@ def create_derived_objects(scene, ob):
return False, None
if ob.dupli_type != 'NONE':
- ob.create_dupli_list(scene)
+ 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.free_dupli_list()
+ ob.dupli_list_clear()
def unpack_list(list_of_tuples):
@@ -116,3 +206,99 @@ def unpack_face_list(list_of_tuples):
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/keyingsets_utils.py b/release/scripts/modules/keyingsets_utils.py
index 00ad45cf9bb..dc61ce2a4af 100644
--- a/release/scripts/modules/keyingsets_utils.py
+++ b/release/scripts/modules/keyingsets_utils.py
@@ -43,7 +43,7 @@ import bpy
# Append the specified property name on the the existing path
def path_add_property(path, prop):
- if len(path):
+ if path:
return path + "." + prop
else:
return prop
@@ -52,16 +52,21 @@ def path_add_property(path, prop):
# Poll Callbacks
-# selected objects
+# selected objects (active object must be in object mode)
def RKS_POLL_selected_objects(ksi, context):
- return context.active_object or len(context.selected_objects)
+ ob = context.active_object
+ if ob:
+ return ob.mode == 'OBJECT'
+ else:
+ return bool(context.selected_objects)
# selected bones
def RKS_POLL_selected_bones(ksi, context):
# we must be in Pose Mode, and there must be some bones selected
- if (context.active_object) and (context.active_object.mode == 'POSE'):
- if context.active_pose_bone or len(context.selected_pose_bones):
+ ob = context.active_object
+ if ob and ob.mode == 'POSE':
+ if context.active_pose_bone or context.selected_pose_bones:
return True
# nothing selected
@@ -78,13 +83,20 @@ def RKS_POLL_selected_items(ksi, context):
# all selected objects or pose bones, depending on which we've got
def RKS_ITER_selected_item(ksi, context, ks):
- if (context.active_object) and (context.active_object.mode == 'POSE'):
+ ob = context.active_object
+ if ob and ob.mode == 'POSE':
for bone in context.selected_pose_bones:
ksi.generate(context, ks, bone)
else:
for ob in context.selected_objects:
ksi.generate(context, ks, ob)
+
+# all select objects only
+def RKS_ITER_selected_objects(ksi, context, ks):
+ for ob in context.selected_objects:
+ ksi.generate(context, ks, ob)
+
###########################
# Generate Callbacks
diff --git a/release/scripts/modules/mesh_utils.py b/release/scripts/modules/mesh_utils.py
new file mode 100644
index 00000000000..5bacff7b0cc
--- /dev/null
+++ b/release/scripts/modules/mesh_utils.py
@@ -0,0 +1,69 @@
+# ##### 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_info.py b/release/scripts/modules/rna_info.py
index e0298d30aa2..93a344f4b09 100644
--- a/release/scripts/modules/rna_info.py
+++ b/release/scripts/modules/rna_info.py
@@ -152,6 +152,14 @@ class InfoStructRNA:
functions.append((identifier, attr))
return functions
+ def get_py_c_functions(self):
+ import types
+ functions = []
+ for identifier, attr in self._get_py_visible_attrs():
+ if type(attr) in (types.BuiltinMethodType, types.BuiltinFunctionType):
+ functions.append((identifier, attr))
+ return functions
+
def __str__(self):
txt = ""
@@ -199,10 +207,15 @@ class InfoPropertyRNA:
self.fixed_type = None
if self.type == "enum":
- self.enum_items[:] = rna_prop.items.keys()
+ self.enum_items[:] = rna_prop.enum_items.keys()
+ self.is_enum_flag = rna_prop.is_enum_flag
+ else:
+ self.is_enum_flag = False
if self.array_length:
self.default = tuple(getattr(rna_prop, "default_array", ()))
+ elif self.type == "enum" and self.is_enum_flag:
+ self.default = getattr(rna_prop, "default_flag", set())
else:
self.default = getattr(rna_prop, "default", None)
self.default_str = "" # fallback
@@ -214,7 +227,10 @@ class InfoPropertyRNA:
elif self.type == "string":
self.default_str = "\"%s\"" % self.default
elif self.type == "enum":
- self.default_str = "'%s'" % self.default
+ if self.is_enum_flag:
+ self.default_str = "%r" % self.default # repr or set()
+ else:
+ self.default_str = "'%s'" % self.default
elif self.array_length:
self.default_str = ''
# special case for floats
@@ -237,7 +253,7 @@ class InfoPropertyRNA:
return "%s=%s" % (self.identifier, default)
return self.identifier
- def get_type_description(self, as_ret=False, as_arg=False, class_fmt="%s"):
+ def get_type_description(self, as_ret=False, as_arg=False, class_fmt="%s", collection_id="Collection"):
type_str = ""
if self.fixed_type is None:
type_str += self.type
@@ -247,7 +263,10 @@ class InfoPropertyRNA:
if self.type in ("float", "int"):
type_str += " in [%s, %s]" % (range_str(self.min), range_str(self.max))
elif self.type == "enum":
- type_str += " in [%s]" % ", ".join(("'%s'" % s) for s in self.enum_items)
+ if self.is_enum_flag:
+ type_str += " set in {%s}" % ", ".join(("'%s'" % s) for s in self.enum_items)
+ else:
+ type_str += " in [%s]" % ", ".join(("'%s'" % s) for s in self.enum_items)
if not (as_arg or as_ret):
# write default property, ignore function args for this
@@ -258,9 +277,9 @@ class InfoPropertyRNA:
else:
if self.type == "collection":
if self.collection_type:
- collection_str = (class_fmt % self.collection_type.identifier) + " collection of "
+ collection_str = (class_fmt % self.collection_type.identifier) + (" %s of " % collection_id)
else:
- collection_str = "Collection of "
+ collection_str = "%s of " % collection_id
else:
collection_str = ""
diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py
index 303277a5d75..1272d81872d 100644
--- a/release/scripts/modules/sys_info.py
+++ b/release/scripts/modules/sys_info.py
@@ -50,11 +50,9 @@ def textWrap(text, length=70):
def write_sysinfo(op):
output_filename = "system-info.txt"
- warnings = 0
- notices = 0
- if output_filename in bpy.data.texts.keys():
- output = bpy.data.texts[output_filename]
+ output = bpy.data.texts.get(output_filename)
+ if output:
output.clear()
else:
output = bpy.data.texts.new(name=output_filename)
@@ -96,16 +94,19 @@ def write_sysinfo(op):
output.write('autosave: {}\n'.format(bpy.utils.user_resource('AUTOSAVE')))
output.write('tempdir: {}\n'.format(bpy.app.tempdir))
- output.write('\nOpenGL\n')
- output.write(lilies)
- output.write('renderer:\t{}\n'.format(bgl.glGetString(bgl.GL_RENDERER)))
- output.write('vendor:\t\t{}\n'.format(bgl.glGetString(bgl.GL_VENDOR)))
- output.write('version:\t{}\n'.format(bgl.glGetString(bgl.GL_VERSION)))
- output.write('extensions:\n')
-
- glext = bgl.glGetString(bgl.GL_EXTENSIONS)
- glext = textWrap(glext, 70)
- for l in glext:
- output.write('\t\t{}\n'.format(l))
+ if bpy.app.background:
+ output.write('\nOpenGL: missing, background mode\n')
+ else:
+ output.write('\nOpenGL\n')
+ output.write(lilies)
+ output.write('renderer:\t{}\n'.format(bgl.glGetString(bgl.GL_RENDERER)))
+ output.write('vendor:\t\t{}\n'.format(bgl.glGetString(bgl.GL_VENDOR)))
+ output.write('version:\t{}\n'.format(bgl.glGetString(bgl.GL_VERSION)))
+ output.write('extensions:\n')
+
+ glext = bgl.glGetString(bgl.GL_EXTENSIONS)
+ glext = textWrap(glext, 70)
+ for l in glext:
+ output.write('\t\t{}\n'.format(l))
op.report({'INFO'}, "System information generated in 'system-info.txt'")
diff --git a/release/scripts/op/fcurve_euler_filter.py b/release/scripts/op/fcurve_euler_filter.py
deleted file mode 100644
index 6332bbe309c..00000000000
--- a/release/scripts/op/fcurve_euler_filter.py
+++ /dev/null
@@ -1,89 +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>
-
-from math import *
-import bpy
-from mathutils import *
-
-
-def main(context):
- def cleanupEulCurve(fcv):
- keys = []
-
- for k in fcv.keyframe_points:
- keys.append([k.handle_left.copy(), k.co.copy(), k.handle_right.copy()])
- print(keys)
-
- for i in range(len(keys)):
- cur = keys[i]
- prev = keys[i - 1] if i > 0 else None
- next = keys[i + 1] if i < len(keys) - 1 else None
-
- if prev is None:
- continue
-
- th = pi
- if abs(prev[1][1] - cur[1][1]) >= th: # more than 180 degree jump
- fac = pi * 2.0
- if prev[1][1] > cur[1][1]:
- while abs(cur[1][1] - prev[1][1]) >= th: # < prev[1][1]:
- cur[0][1] += fac
- cur[1][1] += fac
- cur[2][1] += fac
- elif prev[1][1] < cur[1][1]:
- while abs(cur[1][1] - prev[1][1]) >= th:
- cur[0][1] -= fac
- cur[1][1] -= fac
- cur[2][1] -= fac
-
- for i in range(len(keys)):
- for x in range(2):
- fcv.keyframe_points[i].handle_left[x] = keys[i][0][x]
- fcv.keyframe_points[i].co[x] = keys[i][1][x]
- fcv.keyframe_points[i].handle_right[x] = keys[i][2][x]
-
- flist = bpy.context.active_object.animation_data.action.fcurves
- for f in flist:
- if f.select and f.data_path.endswith("rotation_euler"):
- cleanupEulCurve(f)
-
-
-class DiscontFilterOp(bpy.types.Operator):
- """Fixes the most common causes of gimbal lock in the fcurves of the active bone"""
- bl_idname = "graph.euler_filter"
- bl_label = "Filter out discontinuities in the active fcurves"
-
- @classmethod
- def poll(cls, context):
- return context.active_object != None
-
- def execute(self, context):
- main(context)
- return {'FINISHED'}
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/uv.py b/release/scripts/op/uv.py
deleted file mode 100644
index 83282924b5b..00000000000
--- a/release/scripts/op/uv.py
+++ /dev/null
@@ -1,381 +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 *
-
-
-def write_svg(fw, mesh, image_width, image_height, face_iter):
- # for making an XML compatible string
- from xml.sax.saxutils import escape
- from os.path import basename
-
- fw('<?xml version="1.0" standalone="no"?>\n')
- fw('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" \n')
- fw(' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')
- fw('<svg width="%dpx" height="%dpx" viewBox="0px 0px %dpx %dpx"\n' % (image_width, image_height, image_width, image_height))
- fw(' xmlns="http://www.w3.org/2000/svg" version="1.1">\n')
- desc = "%r, %s, (Blender %s)" % (basename(bpy.data.filepath), mesh.name, bpy.app.version_string)
- fw('<desc>%s</desc>\n' % escape(desc))
-
- # svg colors
- fill_settings = []
- fill_default = 'fill="grey"'
- for mat in mesh.materials if mesh.materials else [None]:
- if mat:
- fill_settings.append('fill="rgb(%d, %d, %d)"' % tuple(int(c * 255) for c in mat.diffuse_color))
- else:
- fill_settings.append(fill_default)
-
- faces = mesh.faces
- for i, uvs in face_iter:
- try: # rare cases material index is invalid.
- fill = fill_settings[faces[i].material_index]
- except IndexError:
- fill = fill_default
-
- fw('<polygon %s fill-opacity="0.5" stroke="black" stroke-width="1px" \n' % fill)
- fw(' points="')
-
- for j, uv in enumerate(uvs):
- x, y = uv[0], 1.0 - uv[1]
- fw('%.3f,%.3f ' % (x * image_width, y * image_height))
- fw('" />\n')
- fw('\n')
- fw('</svg>\n')
-
-
-def write_eps(fw, mesh, image_width, image_height, face_iter):
- fw('%!PS-Adobe-3.0 EPSF-3.0\n')
- fw("%%%%Creator: Blender %s\n" % bpy.app.version_string)
- fw('%%Pages: 1\n')
- fw('%%Orientation: Portrait\n')
- fw("%%%%BoundingBox: 0 0 %d %d\n" % (image_width, image_height))
- fw("%%%%HiResBoundingBox: 0.0 0.0 %.4f %.4f\n" % (image_width, image_height))
- fw('%%EndComments\n')
- fw('%%Page: 1 1\n')
- fw('0 0 translate\n')
- fw('1.0 1.0 scale\n')
- fw('0 0 0 setrgbcolor\n')
- fw('[] 0 setdash\n')
- fw('1 setlinewidth\n')
- fw('1 setlinejoin\n')
- fw('1 setlinecap\n')
- fw('/DRAW {')
- # can remove from here to next comment to disable filling, aparently alpha is not supported
- fw('gsave\n')
- fw('0.7 setgray\n')
- fw('fill\n')
- fw('grestore\n')
- fw('0 setgray\n')
- # remove to here
- fw('stroke\n')
- fw('} def\n')
- fw('newpath\n')
-
- firstline = True
- for i, uvs in face_iter:
- for j, uv in enumerate(uvs):
- x, y = uv[0], uv[1]
- if j == 0:
- if not firstline:
- fw('closepath\n')
- fw('DRAW\n')
- fw('newpath\n')
- firstline = False
- fw('%.5f %.5f moveto\n' % (x * image_width, y * image_height))
- else:
- fw('%.5f %.5f lineto\n' % (x * image_width, y * image_height))
-
- fw('closepath\n')
- fw('DRAW\n')
- fw('showpage\n')
- fw('%%EOF\n')
-
-
-def write_png(fw, mesh_source, image_width, image_height, face_iter):
- filepath = fw.__self__.name
- fw.__self__.close()
-
- material_solids = [bpy.data.materials.new("uv_temp_solid") for i in range(max(1, len(mesh_source.materials)))]
- material_wire = bpy.data.materials.new("uv_temp_wire")
-
- scene = bpy.data.scenes.new("uv_temp")
- mesh = bpy.data.meshes.new("uv_temp")
- for mat_solid in material_solids:
- mesh.materials.append(mat_solid)
-
- tot_verts = 0
- face_lens = []
- for f in mesh_source.faces:
- tot_verts += len(f.vertices)
-
- faces_source = mesh_source.faces
-
- # get unique UV's incase there are many overlapping which slow down filling.
- face_hash_3 = set()
- face_hash_4 = set()
- for i, uv in face_iter:
- material_index = faces_source[i].material_index
- if len(uv) == 3:
- face_hash_3.add((uv[0][0], uv[0][1], uv[1][0], uv[1][1], uv[2][0], uv[2][1], material_index))
- else:
- face_hash_4.add((uv[0][0], uv[0][1], uv[1][0], uv[1][1], uv[2][0], uv[2][1], uv[3][0], uv[3][1], material_index))
-
- # now set the faces coords and locations
- # build mesh data
- mesh_new_vertices = []
- mesh_new_materials = []
- mesh_new_face_vertices = []
-
- current_vert = 0
-
- for face_data in face_hash_3:
- mesh_new_vertices.extend([face_data[0], face_data[1], 0.0, face_data[2], face_data[3], 0.0, face_data[4], face_data[5], 0.0])
- mesh_new_face_vertices.extend([current_vert, current_vert + 1, current_vert + 2, 0])
- mesh_new_materials.append(face_data[6])
- current_vert += 3
- for face_data in face_hash_4:
- mesh_new_vertices.extend([face_data[0], face_data[1], 0.0, face_data[2], face_data[3], 0.0, face_data[4], face_data[5], 0.0, face_data[6], face_data[7], 0.0])
- mesh_new_face_vertices.extend([current_vert, current_vert + 1, current_vert + 2, current_vert + 3])
- mesh_new_materials.append(face_data[8])
- current_vert += 4
-
- mesh.vertices.add(len(mesh_new_vertices) // 3)
- mesh.faces.add(len(mesh_new_face_vertices) // 4)
-
- mesh.vertices.foreach_set("co", mesh_new_vertices)
- mesh.faces.foreach_set("vertices_raw", mesh_new_face_vertices)
- mesh.faces.foreach_set("material_index", mesh_new_materials)
-
- mesh.update(calc_edges=True)
-
- obj_solid = bpy.data.objects.new("uv_temp_solid", mesh)
- obj_wire = bpy.data.objects.new("uv_temp_wire", mesh)
- base_solid = scene.objects.link(obj_solid)
- base_wire = scene.objects.link(obj_wire)
- base_solid.layers[0] = True
- base_wire.layers[0] = True
-
- # place behind the wire
- obj_solid.location = 0, 0, -1
-
- obj_wire.material_slots[0].link = 'OBJECT'
- obj_wire.material_slots[0].material = material_wire
-
- # setup the camera
- cam = bpy.data.cameras.new("uv_temp")
- cam.type = 'ORTHO'
- cam.ortho_scale = 1.0
- obj_cam = bpy.data.objects.new("uv_temp_cam", cam)
- obj_cam.location = 0.5, 0.5, 1.0
- scene.objects.link(obj_cam)
- scene.camera = obj_cam
-
- # setup materials
- for i, mat_solid in enumerate(material_solids):
- if mesh_source.materials and mesh_source.materials[i]:
- mat_solid.diffuse_color = mesh_source.materials[i].diffuse_color
-
- mat_solid.use_shadeless = True
- mat_solid.use_transparency = True
- mat_solid.alpha = 0.25
-
- material_wire.type = 'WIRE'
- material_wire.use_shadeless = True
- material_wire.diffuse_color = 0, 0, 0
-
- # scene render settings
- scene.render.use_raytrace = False
- scene.render.alpha_mode = 'STRAIGHT'
- scene.render.color_mode = 'RGBA'
-
- scene.render.resolution_x = image_width
- scene.render.resolution_y = image_height
- scene.render.resolution_percentage = 100
-
- if image_width > image_height:
- scene.render.pixel_aspect_y = image_width / image_height
- elif image_width < image_height:
- scene.render.pixel_aspect_x = image_height / image_width
-
- scene.frame_start = 1
- scene.frame_end = 1
-
- scene.render.file_format = 'PNG'
- scene.render.filepath = filepath
-
- data_context = {"blend_data": bpy.context.blend_data, "scene": scene}
- bpy.ops.render.render(data_context, write_still=True)
-
- # cleanup
- bpy.data.scenes.remove(scene)
- bpy.data.objects.remove(obj_cam)
- bpy.data.objects.remove(obj_solid)
- bpy.data.objects.remove(obj_wire)
-
- bpy.data.cameras.remove(cam)
- bpy.data.meshes.remove(mesh)
-
- bpy.data.materials.remove(material_wire)
- for mat_solid in material_solids:
- bpy.data.materials.remove(mat_solid)
-
-
-class ExportUVLayout(bpy.types.Operator):
- """Export UV layout to file"""
-
- bl_idname = "uv.export_layout"
- bl_label = "Export UV Layout"
- bl_options = {'REGISTER', 'UNDO'}
-
- filepath = StringProperty(name="File Path", description="File path used for exporting the SVG 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'})
- export_all = BoolProperty(name="All UV's", description="Export all UVs in this mesh (not just the visible ones)", default=False)
- mode = EnumProperty(items=(
- ('SVG', "Scalable Vector Graphic (.svg)", "Export the UV layout to a vector SVG file"),
- ('EPS', "Encapsulate PostScript (.eps)", "Export the UV layout to a vector EPS file"),
- ('PNG', "PNG Image (.png)", "Export the UV layout a bitmap image")),
- name="Format",
- description="File format to export the UV layout to",
- default='PNG')
- size = IntVectorProperty(size=2, default=(1024, 1024), min=8, max=32768, description="Dimensions of the exported file")
-
- @classmethod
- def poll(cls, context):
- obj = context.active_object
- return (obj and obj.type == 'MESH' and obj.data.uv_textures)
-
- def _space_image(self, context):
- space_data = context.space_data
- if isinstance(space_data, bpy.types.SpaceImageEditor):
- return space_data
- else:
- return None
-
- def _image_size(self, context, default_width=1024, default_height=1024):
- # fallback if not in image context.
- image_width, image_height = default_width, default_height
-
- space_data = self._space_image(context)
- if space_data:
- image = space_data.image
- if image:
- width, height = tuple(context.space_data.image.size)
- # incase no data is found.
- if width and height:
- image_width, image_height = width, height
-
- return image_width, image_height
-
- def _face_uv_iter(self, context):
- obj = context.active_object
- mesh = obj.data
- uv_layer = mesh.uv_textures.active.data
- uv_layer_len = len(uv_layer)
-
- if not self.export_all:
-
- local_image = Ellipsis
-
- if context.tool_settings.show_uv_local_view:
- space_data = self._space_image(context)
- if space_data:
- local_image = space_data.image
-
- faces = mesh.faces
-
- for i in range(uv_layer_len):
- uv_elem = uv_layer[i]
- # context checks
- if faces[i].select and (local_image is Ellipsis or local_image == uv_elem.image):
- #~ uv = uv_elem.uv
- #~ if False not in uv_elem.select_uv[:len(uv)]:
- #~ yield (i, uv)
-
- # just write what we see.
- yield (i, uv_layer[i].uv)
- else:
- # all, simple
- for i in range(uv_layer_len):
- yield (i, uv_layer[i].uv)
-
- def execute(self, context):
-
- obj = context.active_object
- is_editmode = (obj.mode == 'EDIT')
- if is_editmode:
- bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
-
- mesh = obj.data
-
- mode = self.mode
-
- filepath = self.filepath
- filepath = bpy.path.ensure_ext(filepath, "." + mode.lower())
- file = open(filepath, "w")
- fw = file.write
-
- if mode == 'SVG':
- func = write_svg
- elif mode == 'EPS':
- func = write_eps
- elif mode == 'PNG':
- func = write_png
-
- func(fw, mesh, self.size[0], self.size[1], self._face_uv_iter(context))
-
- if is_editmode:
- bpy.ops.object.mode_set(mode='EDIT', toggle=False)
-
- return {'FINISHED'}
-
- def check(self, context):
- filepath = bpy.path.ensure_ext(self.filepath, "." + self.mode.lower())
- if filepath != self.filepath:
- self.filepath = filepath
- return True
- else:
- return False
-
- def invoke(self, context, event):
- import os
- self.size = self._image_size(context)
- self.filepath = os.path.splitext(bpy.data.filepath)[0]
- wm = context.window_manager
- wm.fileselect_add(self)
- return {'RUNNING_MODAL'}
-
-
-def menu_func(self, context):
- self.layout.operator(ExportUVLayout.bl_idname)
-
-
-def register():
- bpy.utils.register_module(__name__)
- bpy.types.IMAGE_MT_uvs.append(menu_func)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.IMAGE_MT_uvs.remove(menu_func)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/presets/interaction/blender.py b/release/scripts/presets/interaction/blender.py
index 7ac9cd1eb0b..3b6ce13d7e0 100644
--- a/release/scripts/presets/interaction/blender.py
+++ b/release/scripts/presets/interaction/blender.py
@@ -1,9 +1,6 @@
# Configuration Blender
import bpy
-wm = bpy.context.window_manager
-wm.keyconfigs.active = wm.keyconfigs['Blender']
-
bpy.context.user_preferences.view.use_mouse_auto_depth = False
bpy.context.user_preferences.view.use_zoom_to_mouse = False
bpy.context.user_preferences.view.use_rotate_around_active = False
@@ -13,4 +10,4 @@ bpy.context.user_preferences.inputs.select_mouse = 'RIGHT'
bpy.context.user_preferences.inputs.view_zoom_method = 'DOLLY'
bpy.context.user_preferences.inputs.view_zoom_axis = 'VERTICAL'
bpy.context.user_preferences.inputs.view_rotate_method = 'TRACKBALL'
-bpy.context.user_preferences.inputs.invert_mouse_wheel_zoom = False
+bpy.context.user_preferences.inputs.invert_mouse_zoom = False
diff --git a/release/scripts/presets/interaction/maya.py b/release/scripts/presets/interaction/maya.py
new file mode 100644
index 00000000000..62084645081
--- /dev/null
+++ b/release/scripts/presets/interaction/maya.py
@@ -0,0 +1,10 @@
+# Configuration Blender
+import bpy
+
+bpy.context.user_preferences.edit.use_drag_immediately = True
+bpy.context.user_preferences.edit.use_insertkey_xyz_to_rgb = False
+bpy.context.user_preferences.inputs.select_mouse = 'LEFT'
+bpy.context.user_preferences.inputs.view_zoom_method = 'DOLLY'
+bpy.context.user_preferences.inputs.view_zoom_axis = 'HORIZONTAL'
+bpy.context.user_preferences.inputs.view_rotate_method = 'TURNTABLE'
+bpy.context.user_preferences.inputs.invert_mouse_zoom = True
diff --git a/release/scripts/presets/keyconfig/maya.py b/release/scripts/presets/keyconfig/maya.py
index 120597cdd2a..87f4791ec51 100644
--- a/release/scripts/presets/keyconfig/maya.py
+++ b/release/scripts/presets/keyconfig/maya.py
@@ -7,380 +7,372 @@ kc = wm.keyconfigs.new('maya')
# Map 3D View
km = kc.keymaps.new('3D View', space_type='VIEW_3D', region_type='WINDOW', modal=False)
-kmi = km.items.new('view3d.manipulator', 'LEFTMOUSE', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.manipulator', 'LEFTMOUSE', 'PRESS', any=True)
kmi.properties.release_confirm = True
-kmi = km.items.new('view3d.cursor3d', 'ACTIONMOUSE', 'PRESS')
-kmi = km.items.new('view3d.rotate', 'LEFTMOUSE', 'PRESS', alt=True)
-kmi = km.items.new('view3d.move', 'MIDDLEMOUSE', 'PRESS', alt=True)
-kmi = km.items.new('view3d.zoom', 'RIGHTMOUSE', 'PRESS', alt=True)
-kmi = km.items.new('view3d.view_selected', 'NUMPAD_PERIOD', 'PRESS')
-kmi = km.items.new('view3d.view_center_cursor', 'NUMPAD_PERIOD', 'PRESS', ctrl=True)
-kmi = km.items.new('view3d.fly', 'F', 'PRESS', shift=True)
-kmi = km.items.new('view3d.smoothview', 'TIMER1', 'ANY', any=True)
-kmi = km.items.new('view3d.rotate', 'TRACKPADPAN', 'ANY', alt=True)
-kmi = km.items.new('view3d.rotate', 'MOUSEROTATE', 'ANY')
-kmi = km.items.new('view3d.move', 'TRACKPADPAN', 'ANY')
-kmi = km.items.new('view3d.zoom', 'TRACKPADZOOM', 'ANY')
-kmi = km.items.new('view3d.zoom', 'NUMPAD_PLUS', 'PRESS')
+kmi = km.keymap_items.new('view3d.cursor3d', 'ACTIONMOUSE', 'PRESS')
+kmi = km.keymap_items.new('view3d.rotate', 'LEFTMOUSE', 'PRESS', alt=True)
+kmi = km.keymap_items.new('view3d.move', 'MIDDLEMOUSE', 'PRESS', alt=True)
+kmi = km.keymap_items.new('view3d.zoom', 'RIGHTMOUSE', 'PRESS', alt=True)
+kmi = km.keymap_items.new('view3d.view_selected', 'NUMPAD_PERIOD', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_center_cursor', 'NUMPAD_PERIOD', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.fly', 'F', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.smoothview', 'TIMER1', 'ANY', any=True)
+kmi = km.keymap_items.new('view3d.rotate', 'TRACKPADPAN', 'ANY', alt=True)
+kmi = km.keymap_items.new('view3d.rotate', 'MOUSEROTATE', 'ANY')
+kmi = km.keymap_items.new('view3d.move', 'TRACKPADPAN', 'ANY')
+kmi = km.keymap_items.new('view3d.zoom', 'TRACKPADZOOM', 'ANY')
+kmi = km.keymap_items.new('view3d.zoom', 'NUMPAD_PLUS', 'PRESS')
kmi.properties.delta = 1
-kmi = km.items.new('view3d.zoom', 'NUMPAD_MINUS', 'PRESS')
+kmi = km.keymap_items.new('view3d.zoom', 'NUMPAD_MINUS', 'PRESS')
kmi.properties.delta = -1
-kmi = km.items.new('view3d.zoom', 'EQUAL', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.zoom', 'EQUAL', 'PRESS', ctrl=True)
kmi.properties.delta = 1
-kmi = km.items.new('view3d.zoom', 'MINUS', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.zoom', 'MINUS', 'PRESS', ctrl=True)
kmi.properties.delta = -1
-kmi = km.items.new('view3d.zoom', 'WHEELINMOUSE', 'PRESS')
+kmi = km.keymap_items.new('view3d.zoom', 'WHEELINMOUSE', 'PRESS')
kmi.properties.delta = 1
-kmi = km.items.new('view3d.zoom', 'WHEELOUTMOUSE', 'PRESS')
+kmi = km.keymap_items.new('view3d.zoom', 'WHEELOUTMOUSE', 'PRESS')
kmi.properties.delta = -1
-kmi = km.items.new('view3d.view_all', 'HOME', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_all', 'HOME', 'PRESS')
kmi.properties.center = False
-kmi = km.items.new('view3d.view_all', 'C', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.view_all', 'C', 'PRESS', shift=True)
kmi.properties.center = True
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_0', 'PRESS')
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_0', 'PRESS')
kmi.properties.type = 'CAMERA'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS')
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS')
kmi.properties.type = 'FRONT'
-kmi = km.items.new('view3d.view_orbit', 'NUMPAD_2', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_orbit', 'NUMPAD_2', 'PRESS')
kmi.properties.type = 'ORBITDOWN'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS')
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS')
kmi.properties.type = 'RIGHT'
-kmi = km.items.new('view3d.view_orbit', 'NUMPAD_4', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_orbit', 'NUMPAD_4', 'PRESS')
kmi.properties.type = 'ORBITLEFT'
-kmi = km.items.new('view3d.view_persportho', 'NUMPAD_5', 'PRESS')
-kmi = km.items.new('view3d.view_orbit', 'NUMPAD_6', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_persportho', 'NUMPAD_5', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_orbit', 'NUMPAD_6', 'PRESS')
kmi.properties.type = 'ORBITRIGHT'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS')
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS')
kmi.properties.type = 'TOP'
-kmi = km.items.new('view3d.view_orbit', 'NUMPAD_8', 'PRESS')
+kmi = km.keymap_items.new('view3d.view_orbit', 'NUMPAD_8', 'PRESS')
kmi.properties.type = 'ORBITUP'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', ctrl=True)
kmi.properties.type = 'BACK'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', ctrl=True)
kmi.properties.type = 'LEFT'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', ctrl=True)
kmi.properties.type = 'BOTTOM'
-kmi = km.items.new('view3d.view_pan', 'NUMPAD_2', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'NUMPAD_2', 'PRESS', ctrl=True)
kmi.properties.type = 'PANDOWN'
-kmi = km.items.new('view3d.view_pan', 'NUMPAD_4', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'NUMPAD_4', 'PRESS', ctrl=True)
kmi.properties.type = 'PANLEFT'
-kmi = km.items.new('view3d.view_pan', 'NUMPAD_6', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'NUMPAD_6', 'PRESS', ctrl=True)
kmi.properties.type = 'PANRIGHT'
-kmi = km.items.new('view3d.view_pan', 'NUMPAD_8', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'NUMPAD_8', 'PRESS', ctrl=True)
kmi.properties.type = 'PANUP'
-kmi = km.items.new('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', ctrl=True)
kmi.properties.type = 'PANRIGHT'
-kmi = km.items.new('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True)
kmi.properties.type = 'PANLEFT'
-kmi = km.items.new('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', shift=True)
kmi.properties.type = 'PANUP'
-kmi = km.items.new('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', shift=True)
kmi.properties.type = 'PANDOWN'
-kmi = km.items.new('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', ctrl=True, alt=True)
kmi.properties.type = 'ORBITLEFT'
-kmi = km.items.new('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True, alt=True)
kmi.properties.type = 'ORBITRIGHT'
-kmi = km.items.new('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.type = 'ORBITUP'
-kmi = km.items.new('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.type = 'ORBITDOWN'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True)
kmi.properties.align_active = True
kmi.properties.type = 'FRONT'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True)
kmi.properties.align_active = True
kmi.properties.type = 'RIGHT'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True)
kmi.properties.align_active = True
kmi.properties.type = 'TOP'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True, ctrl=True)
kmi.properties.align_active = True
kmi.properties.type = 'BACK'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True, ctrl=True)
kmi.properties.align_active = True
kmi.properties.type = 'LEFT'
-kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True, ctrl=True)
kmi.properties.align_active = True
kmi.properties.type = 'BOTTOM'
-kmi = km.items.new('view3d.localview', 'NUMPAD_SLASH', 'PRESS')
-kmi = km.items.new('view3d.layers', 'ACCENT_GRAVE', 'PRESS')
+kmi = km.keymap_items.new('view3d.localview', 'NUMPAD_SLASH', 'PRESS')
+kmi = km.keymap_items.new('view3d.layers', 'ACCENT_GRAVE', 'PRESS')
kmi.properties.nr = 0
-kmi = km.items.new('view3d.layers', 'ONE', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'ONE', 'PRESS', any=True)
kmi.properties.nr = 1
-kmi = km.items.new('view3d.layers', 'TWO', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'TWO', 'PRESS', any=True)
kmi.properties.nr = 2
-kmi = km.items.new('view3d.layers', 'THREE', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'THREE', 'PRESS', any=True)
kmi.properties.nr = 3
-kmi = km.items.new('view3d.layers', 'FOUR', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'FOUR', 'PRESS', any=True)
kmi.properties.nr = 4
-kmi = km.items.new('view3d.layers', 'FIVE', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'FIVE', 'PRESS', any=True)
kmi.properties.nr = 5
-kmi = km.items.new('view3d.layers', 'SIX', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'SIX', 'PRESS', any=True)
kmi.properties.nr = 6
-kmi = km.items.new('view3d.layers', 'SEVEN', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'SEVEN', 'PRESS', any=True)
kmi.properties.nr = 7
-kmi = km.items.new('view3d.layers', 'EIGHT', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'EIGHT', 'PRESS', any=True)
kmi.properties.nr = 8
-kmi = km.items.new('view3d.layers', 'NINE', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'NINE', 'PRESS', any=True)
kmi.properties.nr = 9
-kmi = km.items.new('view3d.layers', 'ZERO', 'PRESS', any=True)
+kmi = km.keymap_items.new('view3d.layers', 'ZERO', 'PRESS', any=True)
kmi.properties.nr = 10
-kmi = km.items.new('wm.context_toggle_enum', 'Z', 'PRESS')
+kmi = km.keymap_items.new('wm.context_toggle_enum', 'Z', 'PRESS')
kmi.properties.data_path = 'space_data.viewport_shade'
kmi.properties.value_1 = 'SOLID'
kmi.properties.value_2 = 'WIREFRAME'
-kmi = km.items.new('wm.context_toggle_enum', 'Z', 'PRESS', alt=True)
+kmi = km.keymap_items.new('wm.context_toggle_enum', 'Z', 'PRESS', alt=True)
kmi.properties.data_path = 'space_data.viewport_shade'
kmi.properties.value_1 = 'TEXTURED'
kmi.properties.value_2 = 'SOLID'
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS')
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS')
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True)
kmi.properties.extend = True
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True)
kmi.properties.center = True
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', alt=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', alt=True)
kmi.properties.enumerate = True
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True)
kmi.properties.center = True
kmi.properties.extend = True
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
kmi.properties.center = True
kmi.properties.enumerate = True
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.enumerate = True
kmi.properties.extend = True
-kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
+kmi = km.keymap_items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
kmi.properties.center = True
kmi.properties.enumerate = True
kmi.properties.extend = True
-kmi = km.items.new('view3d.select_border', 'EVT_TWEAK_S', 'ANY')
+kmi = km.keymap_items.new('view3d.select_border', 'EVT_TWEAK_S', 'ANY')
kmi.properties.extend = False
-kmi = km.items.new('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', ctrl=True)
-kmi = km.items.new('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', shift=True, ctrl=True)
+kmi = km.keymap_items.new('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', ctrl=True)
+kmi = km.keymap_items.new('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', shift=True, ctrl=True)
kmi.properties.deselect = True
-kmi = km.items.new('view3d.select_circle', 'C', 'PRESS')
-kmi = km.items.new('view3d.clip_border', 'B', 'PRESS', alt=True)
-kmi = km.items.new('view3d.zoom_border', 'B', 'PRESS', shift=True)
-kmi = km.items.new('view3d.render_border', 'B', 'PRESS', shift=True)
-kmi = km.items.new('view3d.camera_to_view', 'NUMPAD_0', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('view3d.object_as_camera', 'NUMPAD_0', 'PRESS', ctrl=True)
-kmi = km.items.new('wm.call_menu', 'S', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.select_circle', 'C', 'PRESS')
+kmi = km.keymap_items.new('view3d.clip_border', 'B', 'PRESS', alt=True)
+kmi = km.keymap_items.new('view3d.zoom_border', 'B', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.render_border', 'B', 'PRESS', shift=True)
+kmi = km.keymap_items.new('view3d.camera_to_view', 'NUMPAD_0', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('view3d.object_as_camera', 'NUMPAD_0', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'S', 'PRESS', shift=True)
kmi.properties.name = 'VIEW3D_MT_snap'
-kmi = km.items.new('wm.context_set_enum', 'COMMA', 'PRESS')
+kmi = km.keymap_items.new('wm.context_set_enum', 'COMMA', 'PRESS')
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'BOUNDING_BOX_CENTER'
-kmi = km.items.new('wm.context_set_enum', 'COMMA', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.context_set_enum', 'COMMA', 'PRESS', ctrl=True)
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'MEDIAN_POINT'
-kmi = km.items.new('wm.context_toggle', 'COMMA', 'PRESS', alt=True)
+kmi = km.keymap_items.new('wm.context_toggle', 'COMMA', 'PRESS', alt=True)
kmi.properties.data_path = 'space_data.use_pivot_point'
-kmi = km.items.new('wm.context_toggle', 'Q', 'PRESS')
+kmi = km.keymap_items.new('wm.context_toggle', 'Q', 'PRESS')
kmi.properties.data_path = 'space_data.show_manipulator'
-kmi = km.items.new('wm.context_set_enum', 'PERIOD', 'PRESS')
+kmi = km.keymap_items.new('wm.context_set_enum', 'PERIOD', 'PRESS')
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'CURSOR'
-kmi = km.items.new('wm.context_set_enum', 'PERIOD', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.context_set_enum', 'PERIOD', 'PRESS', ctrl=True)
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'INDIVIDUAL_ORIGINS'
-kmi = km.items.new('wm.context_set_enum', 'PERIOD', 'PRESS', alt=True)
+kmi = km.keymap_items.new('wm.context_set_enum', 'PERIOD', 'PRESS', alt=True)
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'ACTIVE_ELEMENT'
-kmi = km.items.new('transform.translate', 'G', 'PRESS', shift=True)
-kmi = km.items.new('transform.translate', 'EVT_TWEAK_S', 'ANY')
-kmi = km.items.new('transform.rotate', 'R', 'PRESS', shift=True)
-kmi = km.items.new('transform.resize', 'S', 'PRESS', shift=True)
-kmi = km.items.new('transform.warp', 'W', 'PRESS', shift=True)
-kmi = km.items.new('transform.tosphere', 'S', 'PRESS', shift=True, alt=True)
-kmi = km.items.new('transform.shear', 'S', 'PRESS', shift=True, ctrl=True, alt=True)
-kmi = km.items.new('transform.select_orientation', 'SPACE', 'PRESS', alt=True)
-kmi = km.items.new('transform.create_orientation', 'SPACE', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('transform.translate', 'G', 'PRESS', shift=True)
+kmi = km.keymap_items.new('transform.translate', 'EVT_TWEAK_S', 'ANY')
+kmi = km.keymap_items.new('transform.rotate', 'R', 'PRESS', shift=True)
+kmi = km.keymap_items.new('transform.resize', 'S', 'PRESS', shift=True)
+kmi = km.keymap_items.new('transform.warp', 'W', 'PRESS', shift=True)
+kmi = km.keymap_items.new('transform.tosphere', 'S', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('transform.shear', 'S', 'PRESS', shift=True, ctrl=True, alt=True)
+kmi = km.keymap_items.new('transform.select_orientation', 'SPACE', 'PRESS', alt=True)
+kmi = km.keymap_items.new('transform.create_orientation', 'SPACE', 'PRESS', ctrl=True, alt=True)
kmi.properties.use = True
-kmi = km.items.new('transform.mirror', 'M', 'PRESS', ctrl=True)
-kmi = km.items.new('wm.context_toggle', 'TAB', 'PRESS', shift=True)
+kmi = km.keymap_items.new('transform.mirror', 'M', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.context_toggle', 'TAB', 'PRESS', shift=True)
kmi.properties.data_path = 'tool_settings.use_snap'
-kmi = km.items.new('transform.snap_type', 'TAB', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('view3d.enable_manipulator', 'W', 'PRESS')
+kmi = km.keymap_items.new('transform.snap_type', 'TAB', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('view3d.enable_manipulator', 'W', 'PRESS')
kmi.properties.translate = True
-kmi = km.items.new('view3d.enable_manipulator', 'E', 'PRESS')
+kmi = km.keymap_items.new('view3d.enable_manipulator', 'E', 'PRESS')
kmi.properties.rotate = True
-kmi = km.items.new('view3d.enable_manipulator', 'R', 'PRESS')
+kmi = km.keymap_items.new('view3d.enable_manipulator', 'R', 'PRESS')
kmi.properties.scale = True
-kmi = km.items.new('view3d.select_border', 'EVT_TWEAK_S', 'ANY', shift=True)
+kmi = km.keymap_items.new('view3d.select_border', 'EVT_TWEAK_S', 'ANY', shift=True)
kmi.properties.extend = True
# Map Object Mode
km = kc.keymaps.new('Object Mode', space_type='EMPTY', region_type='WINDOW', modal=False)
-kmi = km.items.new('wm.context_cycle_enum', 'O', 'PRESS', shift=True)
+kmi = km.keymap_items.new('wm.context_cycle_enum', 'O', 'PRESS', shift=True)
kmi.properties.data_path = 'tool_settings.proportional_edit_falloff'
-kmi = km.items.new('wm.context_toggle_enum', 'O', 'PRESS')
+kmi = km.keymap_items.new('wm.context_toggle_enum', 'O', 'PRESS')
kmi.properties.data_path = 'tool_settings.proportional_edit'
kmi.properties.value_1 = 'DISABLED'
kmi.properties.value_2 = 'ENABLED'
-kmi = km.items.new('view3d.game_start', 'P', 'PRESS')
-kmi = km.items.new('object.select_all', 'A', 'PRESS')
-kmi = km.items.new('object.select_inverse', 'I', 'PRESS', ctrl=True)
-kmi = km.items.new('object.select_linked', 'L', 'PRESS', shift=True)
-kmi = km.items.new('object.select_grouped', 'G', 'PRESS', shift=True)
-kmi = km.items.new('object.select_mirror', 'M', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS')
+kmi = km.keymap_items.new('view3d.game_start', 'P', 'PRESS')
+kmi = km.keymap_items.new('object.select_all', 'A', 'PRESS')
+kmi = km.keymap_items.new('object.select_inverse', 'I', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.select_linked', 'L', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.select_grouped', 'G', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.select_mirror', 'M', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS')
kmi.properties.direction = 'PARENT'
-kmi = km.items.new('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS', shift=True)
kmi.properties.direction = 'PARENT'
kmi.properties.extend = True
-kmi = km.items.new('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS')
+kmi = km.keymap_items.new('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS')
kmi.properties.direction = 'CHILD'
-kmi = km.items.new('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS', shift=True)
kmi.properties.direction = 'CHILD'
kmi.properties.extend = True
-kmi = km.items.new('object.parent_set', 'P', 'PRESS', ctrl=True)
-kmi = km.items.new('object.parent_no_inverse_set', 'P', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('object.parent_clear', 'P', 'PRESS', alt=True)
-kmi = km.items.new('object.track_set', 'T', 'PRESS', ctrl=True)
-kmi = km.items.new('object.track_clear', 'T', 'PRESS', alt=True)
-kmi = km.items.new('object.constraint_add_with_targets', 'C', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('object.constraints_clear', 'C', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('object.location_clear', 'G', 'PRESS', alt=True)
-kmi = km.items.new('object.rotation_clear', 'R', 'PRESS', alt=True)
-kmi = km.items.new('object.scale_clear', 'S', 'PRESS', alt=True)
-kmi = km.items.new('object.origin_clear', 'O', 'PRESS', alt=True)
-kmi = km.items.new('object.hide_view_clear', 'H', 'PRESS', alt=True)
-kmi = km.items.new('object.hide_view_set', 'H', 'PRESS')
-kmi = km.items.new('object.hide_view_set', 'H', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.parent_set', 'P', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.parent_no_inverse_set', 'P', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('object.parent_clear', 'P', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.track_set', 'T', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.track_clear', 'T', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.constraint_add_with_targets', 'C', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('object.constraints_clear', 'C', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('object.location_clear', 'G', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.rotation_clear', 'R', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.scale_clear', 'S', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.origin_clear', 'O', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.hide_view_clear', 'H', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.hide_view_set', 'H', 'PRESS')
+kmi = km.keymap_items.new('object.hide_view_set', 'H', 'PRESS', shift=True)
kmi.properties.unselected = True
-kmi = km.items.new('object.move_to_layer', 'M', 'PRESS')
-kmi = km.items.new('object.delete', 'X', 'PRESS')
-kmi = km.items.new('object.delete', 'DEL', 'PRESS')
-kmi = km.items.new('wm.call_menu', 'A', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.move_to_layer', 'M', 'PRESS')
+kmi = km.keymap_items.new('object.delete', 'X', 'PRESS')
+kmi = km.keymap_items.new('object.delete', 'DEL', 'PRESS')
+kmi = km.keymap_items.new('wm.call_menu', 'A', 'PRESS', shift=True)
kmi.properties.name = 'INFO_MT_add'
-kmi = km.items.new('object.duplicates_make_real', 'A', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('wm.call_menu', 'A', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.duplicates_make_real', 'A', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'A', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_object_apply'
-kmi = km.items.new('wm.call_menu', 'U', 'PRESS')
+kmi = km.keymap_items.new('wm.call_menu', 'U', 'PRESS')
kmi.properties.name = 'VIEW3D_MT_make_single_user'
-kmi = km.items.new('wm.call_menu', 'L', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'L', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_make_links'
-kmi = km.items.new('object.duplicate_move', 'D', 'PRESS', shift=True)
-kmi = km.items.new('object.duplicate_move_linked', 'D', 'PRESS', alt=True)
-kmi = km.items.new('object.join', 'J', 'PRESS', ctrl=True)
-kmi = km.items.new('object.convert', 'C', 'PRESS', alt=True)
-kmi = km.items.new('object.proxy_make', 'P', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('object.make_local', 'L', 'PRESS')
-kmi = km.items.new('anim.keyframe_insert_menu', 'I', 'PRESS')
-kmi = km.items.new('anim.keyframe_delete_v3d', 'I', 'PRESS', alt=True)
-kmi = km.items.new('anim.keying_set_active_set', 'I', 'PRESS', shift=True, ctrl=True, alt=True)
-kmi = km.items.new('group.create', 'G', 'PRESS', ctrl=True)
-kmi = km.items.new('group.objects_remove', 'G', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('group.objects_add_active', 'G', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('group.objects_remove_active', 'G', 'PRESS', shift=True, alt=True)
-kmi = km.items.new('wm.call_menu', 'W', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.duplicate_move', 'D', 'PRESS', shift=True)
+kmi = km.keymap_items.new('object.duplicate_move_linked', 'D', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.join', 'J', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.convert', 'C', 'PRESS', alt=True)
+kmi = km.keymap_items.new('object.proxy_make', 'P', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('object.make_local', 'L', 'PRESS')
+kmi = km.keymap_items.new('anim.keyframe_insert_menu', 'I', 'PRESS')
+kmi = km.keymap_items.new('anim.keyframe_delete_v3d', 'I', 'PRESS', alt=True)
+kmi = km.keymap_items.new('anim.keying_set_active_set', 'I', 'PRESS', shift=True, ctrl=True, alt=True)
+kmi = km.keymap_items.new('group.create', 'G', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('group.objects_remove', 'G', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('group.objects_add_active', 'G', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('group.objects_remove_active', 'G', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('wm.call_menu', 'W', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_object_specials'
-kmi = km.items.new('object.subdivision_set', 'ZERO', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.subdivision_set', 'ZERO', 'PRESS', ctrl=True)
kmi.properties.level = 0
-kmi = km.items.new('object.subdivision_set', 'ONE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.subdivision_set', 'ONE', 'PRESS', ctrl=True)
kmi.properties.level = 1
-kmi = km.items.new('object.subdivision_set', 'TWO', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.subdivision_set', 'TWO', 'PRESS', ctrl=True)
kmi.properties.level = 2
-kmi = km.items.new('object.subdivision_set', 'THREE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.subdivision_set', 'THREE', 'PRESS', ctrl=True)
kmi.properties.level = 3
-kmi = km.items.new('object.subdivision_set', 'FOUR', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.subdivision_set', 'FOUR', 'PRESS', ctrl=True)
kmi.properties.level = 4
-kmi = km.items.new('object.subdivision_set', 'FIVE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.subdivision_set', 'FIVE', 'PRESS', ctrl=True)
kmi.properties.level = 5
-kmi = km.items.new('object.select_all', 'SELECTMOUSE', 'CLICK')
+kmi = km.keymap_items.new('object.select_all', 'SELECTMOUSE', 'CLICK')
kmi.properties.action = 'DESELECT'
# Map Mesh
km = kc.keymaps.new('Mesh', space_type='EMPTY', region_type='WINDOW', modal=False)
-kmi = km.items.new('mesh.loopcut_slide', 'R', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.loop_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('mesh.loop_select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('mesh.loopcut_slide', 'R', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.loop_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('mesh.loop_select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.extend = True
-kmi = km.items.new('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
+kmi = km.keymap_items.new('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
kmi.properties.extend = True
-kmi = km.items.new('mesh.select_shortest_path', 'SELECTMOUSE', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.select_all', 'A', 'PRESS')
-kmi = km.items.new('mesh.select_more', 'NUMPAD_PLUS', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.select_less', 'NUMPAD_MINUS', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.select_inverse', 'I', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.select_non_manifold', 'M', 'PRESS', shift=True, ctrl=True, alt=True)
-kmi = km.items.new('mesh.select_linked', 'L', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.select_linked_pick', 'L', 'PRESS')
-kmi = km.items.new('mesh.select_linked_pick', 'L', 'PRESS', shift=True)
+kmi = km.keymap_items.new('mesh.select_shortest_path', 'SELECTMOUSE', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.select_all', 'A', 'PRESS')
+kmi = km.keymap_items.new('mesh.select_more', 'NUMPAD_PLUS', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.select_less', 'NUMPAD_MINUS', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.select_inverse', 'I', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.select_non_manifold', 'M', 'PRESS', shift=True, ctrl=True, alt=True)
+kmi = km.keymap_items.new('mesh.select_linked', 'L', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.select_linked_pick', 'L', 'PRESS')
+kmi = km.keymap_items.new('mesh.select_linked_pick', 'L', 'PRESS', shift=True)
kmi.properties.deselect = True
-kmi = km.items.new('mesh.faces_select_linked_flat', 'F', 'PRESS', shift=True, ctrl=True, alt=True)
+kmi = km.keymap_items.new('mesh.faces_select_linked_flat', 'F', 'PRESS', shift=True, ctrl=True, alt=True)
kmi.properties.sharpness = 135.0
-kmi = km.items.new('mesh.select_similar', 'G', 'PRESS', shift=True)
-kmi = km.items.new('wm.call_menu', 'TAB', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.select_similar', 'G', 'PRESS', shift=True)
+kmi = km.keymap_items.new('wm.call_menu', 'TAB', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_select_mode'
-kmi = km.items.new('mesh.hide', 'H', 'PRESS')
-kmi = km.items.new('mesh.hide', 'H', 'PRESS', shift=True)
+kmi = km.keymap_items.new('mesh.hide', 'H', 'PRESS')
+kmi = km.keymap_items.new('mesh.hide', 'H', 'PRESS', shift=True)
kmi.properties.unselected = True
-kmi = km.items.new('mesh.reveal', 'H', 'PRESS', alt=True)
-kmi = km.items.new('mesh.normals_make_consistent', 'N', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.normals_make_consistent', 'N', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('mesh.reveal', 'H', 'PRESS', alt=True)
+kmi = km.keymap_items.new('mesh.normals_make_consistent', 'N', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.normals_make_consistent', 'N', 'PRESS', shift=True, ctrl=True)
kmi.properties.inside = True
-kmi = km.items.new('view3d.edit_mesh_extrude_move_normal', 'E', 'PRESS', ctrl=True)
-kmi = km.items.new('view3d.edit_mesh_extrude_individual_move', 'E', 'PRESS', shift=True)
-kmi = km.items.new('wm.call_menu', 'E', 'PRESS', alt=True)
+kmi = km.keymap_items.new('view3d.edit_mesh_extrude_move_normal', 'E', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('view3d.edit_mesh_extrude_individual_move', 'E', 'PRESS', shift=True)
+kmi = km.keymap_items.new('wm.call_menu', 'E', 'PRESS', alt=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_extrude'
-kmi = km.items.new('mesh.spin', 'R', 'PRESS', alt=True)
-kmi = km.items.new('mesh.fill', 'F', 'PRESS', alt=True)
-kmi = km.items.new('mesh.beautify_fill', 'F', 'PRESS', shift=True, alt=True)
-kmi = km.items.new('mesh.quads_convert_to_tris', 'T', 'PRESS', ctrl=True)
-kmi = km.items.new('mesh.tris_convert_to_quads', 'J', 'PRESS', alt=True)
-kmi = km.items.new('mesh.edge_flip', 'F', 'PRESS', shift=True, ctrl=True)
-kmi = km.items.new('mesh.rip_move', 'V', 'PRESS')
-kmi = km.items.new('mesh.merge', 'M', 'PRESS', alt=True)
-kmi = km.items.new('transform.shrink_fatten', 'S', 'PRESS', ctrl=True, alt=True)
-kmi = km.items.new('mesh.edge_face_add', 'F', 'PRESS')
-kmi = km.items.new('mesh.duplicate_move', 'D', 'PRESS', shift=True)
-kmi = km.items.new('wm.call_menu', 'A', 'PRESS', shift=True)
+kmi = km.keymap_items.new('mesh.spin', 'R', 'PRESS', alt=True)
+kmi = km.keymap_items.new('mesh.fill', 'F', 'PRESS', alt=True)
+kmi = km.keymap_items.new('mesh.beautify_fill', 'F', 'PRESS', shift=True, alt=True)
+kmi = km.keymap_items.new('mesh.quads_convert_to_tris', 'T', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('mesh.tris_convert_to_quads', 'J', 'PRESS', alt=True)
+kmi = km.keymap_items.new('mesh.edge_flip', 'F', 'PRESS', shift=True, ctrl=True)
+kmi = km.keymap_items.new('mesh.rip_move', 'V', 'PRESS')
+kmi = km.keymap_items.new('mesh.merge', 'M', 'PRESS', alt=True)
+kmi = km.keymap_items.new('transform.shrink_fatten', 'S', 'PRESS', ctrl=True, alt=True)
+kmi = km.keymap_items.new('mesh.edge_face_add', 'F', 'PRESS')
+kmi = km.keymap_items.new('mesh.duplicate_move', 'D', 'PRESS', shift=True)
+kmi = km.keymap_items.new('wm.call_menu', 'A', 'PRESS', shift=True)
kmi.properties.name = 'INFO_MT_mesh_add'
-kmi = km.items.new('mesh.separate', 'P', 'PRESS')
-kmi = km.items.new('mesh.split', 'Y', 'PRESS')
-kmi = km.items.new('mesh.dupli_extrude_cursor', 'ACTIONMOUSE', 'CLICK', ctrl=True)
-kmi = km.items.new('mesh.delete', 'X', 'PRESS')
-kmi = km.items.new('mesh.delete', 'DEL', 'PRESS')
-kmi = km.items.new('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', key_modifier='K')
-kmi = km.items.new('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', shift=True, key_modifier='K')
+kmi = km.keymap_items.new('mesh.separate', 'P', 'PRESS')
+kmi = km.keymap_items.new('mesh.split', 'Y', 'PRESS')
+kmi = km.keymap_items.new('mesh.dupli_extrude_cursor', 'ACTIONMOUSE', 'CLICK', ctrl=True)
+kmi = km.keymap_items.new('mesh.delete', 'X', 'PRESS')
+kmi = km.keymap_items.new('mesh.delete', 'DEL', 'PRESS')
+kmi = km.keymap_items.new('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', key_modifier='K')
+kmi = km.keymap_items.new('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', shift=True, key_modifier='K')
kmi.properties.type = 'MIDPOINTS'
-kmi = km.items.new('object.vertex_parent_set', 'P', 'PRESS', ctrl=True)
-kmi = km.items.new('wm.call_menu', 'W', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('object.vertex_parent_set', 'P', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'W', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_specials'
-kmi = km.items.new('wm.call_menu', 'F', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'F', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_faces'
-kmi = km.items.new('wm.call_menu', 'E', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'E', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_edges'
-kmi = km.items.new('wm.call_menu', 'V', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'V', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_vertices'
-kmi = km.items.new('wm.call_menu', 'H', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'H', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_hook'
-kmi = km.items.new('wm.call_menu', 'U', 'PRESS')
+kmi = km.keymap_items.new('wm.call_menu', 'U', 'PRESS')
kmi.properties.name = 'VIEW3D_MT_uv_map'
-kmi = km.items.new('wm.call_menu', 'G', 'PRESS', ctrl=True)
+kmi = km.keymap_items.new('wm.call_menu', 'G', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_vertex_group'
-kmi = km.items.new('wm.context_cycle_enum', 'O', 'PRESS', shift=True)
+kmi = km.keymap_items.new('wm.context_cycle_enum', 'O', 'PRESS', shift=True)
kmi.properties.data_path = 'tool_settings.proportional_edit_falloff'
-kmi = km.items.new('wm.context_toggle_enum', 'O', 'PRESS')
+kmi = km.keymap_items.new('wm.context_toggle_enum', 'O', 'PRESS')
kmi.properties.data_path = 'tool_settings.proportional_edit'
kmi.properties.value_1 = 'DISABLED'
kmi.properties.value_2 = 'ENABLED'
-kmi = km.items.new('wm.context_toggle_enum', 'O', 'PRESS', alt=True)
+kmi = km.keymap_items.new('wm.context_toggle_enum', 'O', 'PRESS', alt=True)
kmi.properties.data_path = 'tool_settings.proportional_edit'
kmi.properties.value_1 = 'DISABLED'
kmi.properties.value_2 = 'CONNECTED'
-kmi = km.items.new('mesh.select_all', 'SELECTMOUSE', 'CLICK')
+kmi = km.keymap_items.new('mesh.select_all', 'SELECTMOUSE', 'CLICK')
kmi.properties.action = 'DESELECT'
wm.keyconfigs.active = kc
-
-bpy.context.user_preferences.edit.use_drag_immediately = True
-bpy.context.user_preferences.edit.use_insertkey_xyz_to_rgb = False
-bpy.context.user_preferences.inputs.select_mouse = 'LEFT'
-bpy.context.user_preferences.inputs.view_zoom_method = 'DOLLY'
-bpy.context.user_preferences.inputs.view_zoom_axis = 'HORIZONTAL'
-bpy.context.user_preferences.inputs.view_rotate_method = 'TURNTABLE'
-bpy.context.user_preferences.inputs.invert_mouse_wheel_zoom = True
diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py
new file mode 100644
index 00000000000..d5f7a63366a
--- /dev/null
+++ b/release/scripts/startup/bl_operators/__init__.py
@@ -0,0 +1,58 @@
+# ##### 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>
+
+if "bpy" in locals():
+ from imp import reload as _reload
+ for val in _modules_loaded.values():
+ _reload(val)
+_modules = (
+ "add_mesh_torus",
+ "animsys_update",
+ "image",
+ "mesh",
+ "nla",
+ "object_align",
+ "object",
+ "object_randomize_transform",
+ "object_quick_effects",
+ "presets",
+ "screen_play_rendered_anim",
+ "sequencer",
+ "uvcalc_follow_active",
+ "uvcalc_lightmap",
+ "uvcalc_smart_project",
+ "vertexpaint_dirt",
+ "wm",
+)
+__import__(name=__name__, fromlist=_modules)
+_namespace = globals()
+_modules_loaded = {name: _namespace[name] for name in _modules}
+del _namespace
+
+
+import bpy
+
+
+def register():
+ bpy.utils.register_module(__name__)
+
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
diff --git a/release/scripts/op/add_mesh_torus.py b/release/scripts/startup/bl_operators/add_mesh_torus.py
index db4f90f05d1..460330a56a1 100644
--- a/release/scripts/op/add_mesh_torus.py
+++ b/release/scripts/startup/bl_operators/add_mesh_torus.py
@@ -19,10 +19,11 @@
# <pep8 compliant>
import bpy
import mathutils
-from math import cos, sin, pi
def add_torus(major_rad, minor_rad, major_seg, minor_seg):
+ from math import cos, sin, pi
+
Vector = mathutils.Vector
Quaternion = mathutils.Quaternion
@@ -71,7 +72,7 @@ def add_torus(major_rad, minor_rad, major_seg, minor_seg):
return verts, faces
-from bpy.props import *
+from bpy.props import FloatProperty, IntProperty, BoolProperty, FloatVectorProperty
class AddTorus(bpy.types.Operator):
@@ -135,20 +136,3 @@ class AddTorus(bpy.types.Operator):
add_object_utils.object_data_add(context, mesh, operator=self)
return {'FINISHED'}
-
-
-def menu_func(self, context):
- self.layout.operator(AddTorus.bl_idname, text="Torus", icon='MESH_TORUS')
-
-
-def register():
- bpy.utils.register_class(AddTorus)
- bpy.types.INFO_MT_mesh_add.append(menu_func)
-
-
-def unregister():
- bpy.utils.unregister_class(AddTorus)
- bpy.types.INFO_MT_mesh_add.remove(menu_func)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/animsys_update.py b/release/scripts/startup/bl_operators/animsys_update.py
index 9262ff40a37..63d438a5066 100644
--- a/release/scripts/op/animsys_update.py
+++ b/release/scripts/startup/bl_operators/animsys_update.py
@@ -237,14 +237,16 @@ data_path_update = [
("ImageTexture", "mirror_y", "use_mirror_y"),
("ImageTexture", "normal_map", "use_normal_map"),
("MarbleTexture", "noise_size", "noise_scale"),
- ("MarbleTexture", "noisebasis2", "noisebasis_2"),
+ ("MarbleTexture", "noisebasis2", "noise_basis_2"),
+ ("MarbleTexture", "noisebasis_2", "noise_basis_2"),
("MusgraveTexture", "highest_dimension", "dimension_max"),
("MusgraveTexture", "noise_size", "noise_scale"),
("StucciTexture", "noise_size", "noise_scale"),
("VoronoiTexture", "coloring", "color_mode"),
("VoronoiTexture", "noise_size", "noise_scale"),
("WoodTexture", "noise_size", "noise_scale"),
- ("WoodTexture", "noisebasis2", "noisebasis_2"),
+ ("WoodTexture", "noisebasis2", "noise_basis_2"),
+ ("WoodTexture", "noisebasis_2", "noise_basis_2"),
("World", "blend_sky", "use_sky_blend"),
("World", "paper_sky", "use_sky_paper"),
("World", "real_sky", "use_sky_real"),
@@ -679,6 +681,7 @@ data_path_update = [
("SpeedControlSequence", "global_speed", "multiply_speed"),
("SpeedControlSequence", "use_curve_velocity", "use_as_speed"),
("SpeedControlSequence", "use_curve_compress_y", "scale_to_length"),
+ ("Key", "keys", "key_blocks"),
]
@@ -686,7 +689,7 @@ import bpy
class UpdateAnimData(bpy.types.Operator):
- '''Update data paths from 2.53 to edited data paths of drivers and fcurves'''
+ """Update data paths from 2.56 and previous versions, modifying data paths of drivers and fcurves"""
bl_idname = "anim.update_data_paths"
bl_label = "Update Animation Data"
@@ -694,15 +697,3 @@ class UpdateAnimData(bpy.types.Operator):
import animsys_refactor
animsys_refactor.update_data_paths(data_path_update)
return {'FINISHED'}
-
-
-if __name__ == "__main__":
- bpy.ops.anim.update_data_paths()
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
diff --git a/release/scripts/op/image.py b/release/scripts/startup/bl_operators/image.py
index e1d471f8254..462db3a2c5e 100644
--- a/release/scripts/op/image.py
+++ b/release/scripts/startup/bl_operators/image.py
@@ -31,25 +31,20 @@ class EditExternally(bpy.types.Operator):
filepath = StringProperty(name="File Path", description="Path to an image file", maxlen=1024, default="")
def _editor_guess(self, context):
- import platform
- try:
- system = platform.system()
- except UnicodeDecodeError:
- import sys
- system = sys.platform
+ import sys
image_editor = context.user_preferences.filepaths.image_editor
# use image editor in the preferences when available.
if not image_editor:
- if system in ('Windows', 'win32'):
+ if sys.platform[:3] == "win":
image_editor = ["start"] # not tested!
- elif system == 'Darwin':
+ elif sys.platform == "darwin":
image_editor = ["open"]
else:
image_editor = ["gimp"]
else:
- if system == 'Darwin':
+ if sys.platform == "darwin":
# blender file selector treats .app as a folder
# and will include a trailing backslash, so we strip it.
image_editor.rstrip('\\')
@@ -65,12 +60,18 @@ class EditExternally(bpy.types.Operator):
filepath = bpy.path.abspath(self.filepath)
if not os.path.exists(filepath):
- self.report('ERROR', "Image path '%s' not found." % filepath)
+ self.report({'ERROR'}, "Image path %r not found." % filepath)
return {'CANCELLED'}
cmd = self._editor_guess(context) + [filepath]
- subprocess.Popen(cmd)
+ try:
+ subprocess.Popen(cmd)
+ except:
+ import traceback
+ traceback.print_exc()
+ self.report({'ERROR'}, "Image editor not found, please specify in User Preferences > File")
+ return {'CANCELLED'}
return {'FINISHED'}
@@ -78,6 +79,8 @@ class EditExternally(bpy.types.Operator):
try:
filepath = context.space_data.image.filepath
except:
+ import traceback
+ traceback.print_exc()
self.report({'ERROR'}, "Image not found on disk")
return {'CANCELLED'}
@@ -109,7 +112,7 @@ class SaveDirty(bpy.types.Operator):
class ProjectEdit(bpy.types.Operator):
- """Edit a snapshot if the viewport in an external image editor"""
+ """Edit a snapshot of the viewport in an external image editor"""
bl_idname = "image.project_edit"
bl_label = "Project Edit"
bl_options = {'REGISTER'}
@@ -168,7 +171,10 @@ class ProjectEdit(bpy.types.Operator):
image_new.file_format = 'PNG'
image_new.save()
- bpy.ops.image.external_edit(filepath=filepath_final)
+ try:
+ bpy.ops.image.external_edit(filepath=filepath_final)
+ except RuntimeError as re:
+ self.report({'ERROR'}, str(re))
return {'FINISHED'}
@@ -185,6 +191,8 @@ class ProjectApply(bpy.types.Operator):
try:
image = bpy.data.images[image_name]
except KeyError:
+ import traceback
+ traceback.print_exc()
self.report({'ERROR'}, "Could not find image '%s'" % image_name)
return {'CANCELLED'}
@@ -192,14 +200,3 @@ class ProjectApply(bpy.types.Operator):
bpy.ops.paint.project_image(image=image_name)
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/mesh.py b/release/scripts/startup/bl_operators/mesh.py
index 4906fd7fc6c..44d81ba53df 100644
--- a/release/scripts/op/mesh.py
+++ b/release/scripts/startup/bl_operators/mesh.py
@@ -20,6 +20,8 @@
import bpy
+from bpy.props import EnumProperty
+
class MeshSelectInteriorFaces(bpy.types.Operator):
'''Select faces where all edges have more then 2 face users.'''
@@ -66,17 +68,23 @@ class MeshSelectInteriorFaces(bpy.types.Operator):
class MeshMirrorUV(bpy.types.Operator):
'''Copy mirror UV coordinates on the X axis based on a mirrored mesh'''
- bl_idname = "mesh.faces_miror_uv"
+ bl_idname = "mesh.faces_mirror_uv"
bl_label = "Copy Mirrored UV coords"
bl_options = {'REGISTER', 'UNDO'}
+ direction = EnumProperty(items=(
+ ('POSITIVE', "Positive", ""),
+ ('NEGATIVE', "Negative", "")),
+ name="Axis Direction",
+ description="")
+
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH')
def execute(self, context):
- DIR = 1 # TODO, make an option
+ DIR = (self.direction == 'NEGATIVE')
from mathutils import Vector
@@ -129,7 +137,7 @@ class MeshMirrorUV(bpy.types.Operator):
# find mirror faces
mirror_fm = {}
for i, f in enumerate(faces):
- verts = f.vertices[:]
+ verts = list(f.vertices)
verts.sort()
verts = tuple(verts)
mirror_fm[verts] = i
@@ -170,14 +178,3 @@ class MeshMirrorUV(bpy.types.Operator):
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/nla.py b/release/scripts/startup/bl_operators/nla.py
index 5711fdf12e5..923ca92a162 100644
--- a/release/scripts/op/nla.py
+++ b/release/scripts/startup/bl_operators/nla.py
@@ -98,7 +98,7 @@ def bake(frame_start, frame_end, step=1, only_selected=False):
pose_items = pose.bones.items()
for name, pbone in pose_items:
- if only_selected and not pbone.select:
+ if only_selected and not pbone.bone.select:
continue
for f in frame_range:
@@ -124,7 +124,7 @@ def bake(frame_start, frame_end, step=1, only_selected=False):
return action
-from bpy.props import *
+from bpy.props import IntProperty, BoolProperty
class BakeAction(bpy.types.Operator):
@@ -168,18 +168,3 @@ class BakeAction(bpy.types.Operator):
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
-
-
-#def menu_func(self, context):
-# self.layout.operator(BakeAction.bl_idname, text="Bake Armature Action")
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/object.py b/release/scripts/startup/bl_operators/object.py
index 92886689972..0342a14a1b2 100644
--- a/release/scripts/op/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -19,7 +19,7 @@
# <pep8 compliant>
import bpy
-from bpy.props import *
+from bpy.props import StringProperty, BoolProperty, EnumProperty, IntProperty
class SelectPattern(bpy.types.Operator):
@@ -177,6 +177,9 @@ class SubdivisionSet(bpy.types.Operator):
if relative and level == 0:
return {'CANCELLED'} # nothing to do
+ if not relative and level < 0:
+ self.level = level = 0
+
def set_object_subd(obj):
for mod in obj.modifiers:
if mod.type == 'MULTIRES':
@@ -250,11 +253,11 @@ class ShapeTransfer(bpy.types.Operator):
def ob_add_shape(ob, name):
me = ob.data
key = ob.shape_key_add(from_mix=False)
- if len(me.shape_keys.keys) == 1:
+ if len(me.shape_keys.key_blocks) == 1:
key.name = "Basis"
key = ob.shape_key_add(from_mix=False) # we need a rest
key.name = name
- ob.active_shape_key_index = len(me.shape_keys.keys) - 1
+ ob.active_shape_key_index = len(me.shape_keys.key_blocks) - 1
ob.show_only_shape_key = True
from mathutils.geometry import barycentric_transform
@@ -270,7 +273,7 @@ class ShapeTransfer(bpy.types.Operator):
orig_normals = me_nos(me.vertices)
# orig_coords = me_cos(me.vertices) # the actual mverts location isnt as relyable as the base shape :S
- orig_coords = me_cos(me.shape_keys.keys[0].data)
+ orig_coords = me_cos(me.shape_keys.key_blocks[0].data)
for ob_other in objects:
me_other = ob_other.data
@@ -280,7 +283,7 @@ class ShapeTransfer(bpy.types.Operator):
target_normals = me_nos(me_other.vertices)
if me_other.shape_keys:
- target_coords = me_cos(me_other.shape_keys.keys[0].data)
+ target_coords = me_cos(me_other.shape_keys.key_blocks[0].data)
else:
target_coords = me_cos(me_other.vertices)
@@ -476,7 +479,6 @@ class MakeDupliFace(bpy.types.Operator):
def _main(self, context):
from mathutils import Vector
- from math import sqrt
SCALE_FAC = 0.01
offset = 0.5 * SCALE_FAC
@@ -562,14 +564,3 @@ class ClearAllRestrictRender(bpy.types.Operator):
for obj in context.scene.objects:
obj.hide_render = False
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/object_align.py b/release/scripts/startup/bl_operators/object_align.py
index d98fe584c1a..644f30a4745 100644
--- a/release/scripts/op/object_align.py
+++ b/release/scripts/startup/bl_operators/object_align.py
@@ -230,7 +230,7 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to):
return True
-from bpy.props import *
+from bpy.props import EnumProperty
class AlignObjects(bpy.types.Operator):
@@ -239,7 +239,7 @@ class AlignObjects(bpy.types.Operator):
bl_label = "Align Objects"
bl_options = {'REGISTER', 'UNDO'}
- align_mode = bpy.props.EnumProperty(items=(
+ align_mode = EnumProperty(items=(
('OPT_1', "Negative Sides", ""),
('OPT_2', "Centers", ""),
('OPT_3', "Positive Sides", "")),
@@ -247,7 +247,7 @@ class AlignObjects(bpy.types.Operator):
description="",
default='OPT_2')
- relative_to = bpy.props.EnumProperty(items=(
+ relative_to = EnumProperty(items=(
('OPT_1', "Scene Origin", ""),
('OPT_2', "3D Cursor", ""),
('OPT_3', "Selection", ""),
@@ -278,23 +278,3 @@ class AlignObjects(bpy.types.Operator):
return {'CANCELLED'}
else:
return {'FINISHED'}
-
-
-def menu_func(self, context):
- if context.mode == 'OBJECT':
- self.layout.operator(AlignObjects.bl_idname,
- text="Align Objects")
-
-
-def register():
- bpy.utils.register_module(__name__)
- bpy.types.VIEW3D_MT_transform.append(menu_func)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.VIEW3D_MT_transform.remove(menu_func)
-
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/startup/bl_operators/object_quick_effects.py b/release/scripts/startup/bl_operators/object_quick_effects.py
new file mode 100644
index 00000000000..21640fa3ee6
--- /dev/null
+++ b/release/scripts/startup/bl_operators/object_quick_effects.py
@@ -0,0 +1,296 @@
+# ##### 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>
+
+from mathutils import Vector
+import bpy
+from bpy.props import BoolProperty, EnumProperty, IntProperty, FloatProperty, FloatVectorProperty
+
+
+class MakeFur(bpy.types.Operator):
+ bl_idname = "object.make_fur"
+ bl_label = "Make Fur"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ density = EnumProperty(items=(
+ ('LIGHT', "Light", ""),
+ ('MEDIUM', "Medium", ""),
+ ('HEAVY', "Heavy", "")),
+ name="Fur Density",
+ description="",
+ default='MEDIUM')
+
+ view_percentage = IntProperty(name="View %",
+ default=10, min=1, max=100, soft_min=1, soft_max=100)
+
+ length = FloatProperty(name="Length",
+ default=0.1, min=0.001, max=100, soft_min=0.01, soft_max=10)
+
+ def execute(self, context):
+ fake_context = bpy.context.copy()
+ 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.")
+ return {'CANCELLED'}
+
+ mat = bpy.data.materials.new("Fur Material")
+ mat.strand.tip_size = 0.25
+ mat.strand.blend_distance = 0.5
+
+ for obj in mesh_objects:
+ fake_context["object"] = obj
+ bpy.ops.object.particle_system_add(fake_context)
+
+ psys = obj.particle_systems[-1]
+ psys.settings.type = 'HAIR'
+
+ if self.density == 'LIGHT':
+ psys.settings.count = 100
+ elif self.density == 'MEDIUM':
+ psys.settings.count = 1000
+ elif self.density == 'HEAVY':
+ psys.settings.count = 10000
+
+ psys.settings.child_nbr = self.view_percentage
+ psys.settings.hair_length = self.length
+ psys.settings.use_strand_primitive = True
+ psys.settings.use_hair_bspline = True
+ psys.settings.child_type = 'INTERPOLATED'
+
+ obj.data.materials.append(mat)
+ obj.particle_systems[-1].settings.material = len(obj.data.materials)
+
+ return {'FINISHED'}
+
+
+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
+
+ min_co[0] = min(bb_vec[0], min_co[0])
+ min_co[1] = min(bb_vec[1], min_co[1])
+ min_co[2] = min(bb_vec[2], min_co[2])
+ max_co[0] = max(bb_vec[0], max_co[0])
+ max_co[1] = max(bb_vec[1], max_co[1])
+ max_co[2] = max(bb_vec[2], max_co[2])
+
+
+class MakeSmoke(bpy.types.Operator):
+ bl_idname = "object.make_smoke"
+ bl_label = "Make 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)
+
+ 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))
+
+ if not mesh_objects:
+ self.report({'ERROR'}, "Select at least one mesh object.")
+ return {'CANCELLED'}
+
+ for obj in mesh_objects:
+ fake_context["object"] = obj
+ # make each selected object a smoke flow
+ bpy.ops.object.modifier_add(fake_context, type='SMOKE')
+ obj.modifiers[-1].smoke_type = 'FLOW'
+
+ psys = obj.particle_systems[-1]
+ if self.style == 'PUFF':
+ psys.settings.frame_end = psys.settings.frame_start
+ psys.settings.emit_from = 'VOLUME'
+ psys.settings.distribution = 'RAND'
+ elif self.style == 'FIRE':
+ psys.settings.effector_weights.gravity = -1
+ psys.settings.lifetime = 5
+ psys.settings.count = 100000
+
+ obj.modifiers[-2].flow_settings.initial_velocity = True
+ obj.modifiers[-2].flow_settings.temperature = 2
+
+ psys.settings.use_render_emitter = self.show_flows
+ if not self.show_flows:
+ obj.draw_type = 'WIRE'
+
+ # store bounding box min/max for the domain object
+ obj_bb_minmax(obj, min_co, max_co)
+
+ # add the smoke domain object
+ bpy.ops.mesh.primitive_cube_add()
+ obj = context.active_object
+ obj.name = "Smoke Domain"
+
+ # give the smoke some room above the flows
+ obj.location = 0.5 * (max_co + min_co) + Vector((0.0, 0.0, 1.0))
+ obj.scale = 0.5 * (max_co - min_co) + Vector((1.0, 1.0, 2.0))
+
+ # setup smoke domain
+ bpy.ops.object.modifier_add(type='SMOKE')
+ obj.modifiers[-1].smoke_type = 'DOMAIN'
+ if self.style == 'FIRE':
+ obj.modifiers[-1].domain_settings.use_dissolve_smoke = True
+ obj.modifiers[-1].domain_settings.dissolve_speed = 20
+ obj.modifiers[-1].domain_settings.use_high_resolution = True
+
+ # create a volume material with a voxel data texture for the domain
+ bpy.ops.object.material_slot_add()
+
+ mat = bpy.data.materials.new("Smoke Domain Material")
+ obj.material_slots[0].material = mat
+ mat.type = 'VOLUME'
+ 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
+
+ # 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
+
+ ramp = mat.texture_slots[1].texture.color_ramp
+
+ elem = ramp.elements.new(0.333)
+ elem.color[0] = elem.color[3] = 1
+ elem.color[1] = elem.color[2] = 0
+
+ elem = ramp.elements.new(0.666)
+ elem.color[0] = elem.color[1] = elem.color[3] = 1
+ elem.color[2] = 0
+
+ mat.texture_slots[1].use_map_emission = True
+ mat.texture_slots[1].blend_type = 'MULTIPLY'
+
+ return {'FINISHED'}
+
+
+class MakeFluid(bpy.types.Operator):
+ bl_idname = "object.make_fluid"
+ bl_label = "Make Fluid"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ 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)
+
+ 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)]
+ min_co = Vector((100000, 100000, 100000))
+ max_co = Vector((-100000, -100000, -100000))
+
+ if not mesh_objects:
+ self.report({'ERROR'}, "Select at least one mesh object.")
+ return {'CANCELLED'}
+
+ for obj in mesh_objects:
+ fake_context["object"] = obj
+ # 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
+ for mod in obj.modifiers:
+ if mod.type == 'FLUID_SIMULATION':
+ break
+
+ if self.style == 'INFLOW':
+ mod.settings.type = 'INFLOW'
+ mod.settings.inflow_velocity = self.initial_velocity.copy()
+ else:
+ mod.settings.type = 'FLUID'
+ mod.settings.initial_velocity = self.initial_velocity.copy()
+
+ obj.hide_render = not self.show_flows
+ if not self.show_flows:
+ obj.draw_type = 'WIRE'
+
+ # store bounding box min/max for the domain object
+ obj_bb_minmax(obj, min_co, max_co)
+
+ # add the fluid domain object
+ bpy.ops.mesh.primitive_cube_add()
+ obj = context.active_object
+ obj.name = "Fluid Domain"
+
+ # 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])))
+
+ # setup smoke domain
+ bpy.ops.object.modifier_add(type='FLUID_SIMULATION')
+ obj.modifiers[-1].settings.type = 'DOMAIN'
+
+ # make the domain smooth so it renders nicely
+ bpy.ops.object.shade_smooth()
+
+ # create a ray-transparent material for the domain
+ bpy.ops.object.material_slot_add()
+
+ mat = bpy.data.materials.new("Fluid Domain Material")
+ obj.material_slots[0].material = mat
+
+ mat.specular_intensity = 1
+ mat.specular_hardness = 100
+ mat.use_transparency = True
+ mat.alpha = 0.0
+ mat.transparency_method = 'RAYTRACE'
+ mat.raytrace_transparency.ior = 1.33
+ mat.raytrace_transparency.depth = 4
+
+ if self.start_baking:
+ bpy.ops.fluid.bake()
+
+ return {'FINISHED'}
diff --git a/release/scripts/op/object_randomize_transform.py b/release/scripts/startup/bl_operators/object_randomize_transform.py
index ee7a5f98b91..9dc5086086f 100644
--- a/release/scripts/op/object_randomize_transform.py
+++ b/release/scripts/startup/bl_operators/object_randomize_transform.py
@@ -84,7 +84,7 @@ def randomize_selected(seed, delta, loc, rot, scale, scale_even):
uniform(0.0, 0.0), uniform(0.0, 0.0), uniform(0.0, 0.0)
-from bpy.props import *
+from bpy.props import IntProperty, BoolProperty, FloatVectorProperty
class RandomizeLocRotSize(bpy.types.Operator):
@@ -145,23 +145,3 @@ class RandomizeLocRotSize(bpy.types.Operator):
randomize_selected(seed, delta, loc, rot, scale, scale_even)
return {'FINISHED'}
-
-
-def menu_func(self, context):
- if context.mode == 'OBJECT':
- self.layout.operator(RandomizeLocRotSize.bl_idname,
- text="Randomize Transform")
-
-
-def register():
- bpy.utils.register_module(__name__)
- bpy.types.VIEW3D_MT_transform.append(menu_func)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.VIEW3D_MT_transform.remove(menu_func)
-
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/presets.py b/release/scripts/startup/bl_operators/presets.py
index 65653aeee3e..493c51ad237 100644
--- a/release/scripts/op/presets.py
+++ b/release/scripts/startup/bl_operators/presets.py
@@ -19,7 +19,6 @@
# <pep8 compliant>
import bpy
-import os
class AddPresetBase():
@@ -49,11 +48,11 @@ class AddPresetBase():
preset_menu_class = getattr(bpy.types, self.preset_menu)
if not self.remove_active:
-
- if not self.name:
+ name = self.name.strip()
+ if not name:
return {'FINISHED'}
- filename = self.as_filename(self.name)
+ filename = self.as_filename(name)
target_path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", self.preset_subdir), create=True)
@@ -119,7 +118,7 @@ class AddPresetBase():
return {'FINISHED'}
def check(self, context):
- self.name = self.as_filename(self.name)
+ self.name = self.as_filename(self.name.strip())
def invoke(self, context, event):
if not self.remove_active:
@@ -243,10 +242,10 @@ class AddPresetSunSky(AddPresetBase, bpy.types.Operator):
"sky.sun_brightness",
"sky.sun_intensity",
"sky.sun_size",
- "sky.use_sky_blend",
- "sky.use_sky_blend_type",
- "sky.use_sky_color_space",
- "sky.use_sky_exposure",
+ "sky.sky_blend",
+ "sky.sky_blend_type",
+ "sky.sky_color_space",
+ "sky.sky_exposure",
]
preset_subdir = "sunsky"
@@ -265,7 +264,7 @@ class AddPresetInteraction(AddPresetBase, bpy.types.Operator):
preset_values = [
"user_preferences.edit.use_drag_immediately",
"user_preferences.edit.use_insertkey_xyz_to_rgb",
- "user_preferences.inputs.invert_mouse_wheel_zoom",
+ "user_preferences.inputs.invert_mouse_zoom",
"user_preferences.inputs.select_mouse",
"user_preferences.inputs.use_emulate_numpad",
"user_preferences.inputs.use_mouse_continuous",
@@ -328,7 +327,7 @@ class AddPresetOperator(AddPresetBase, bpy.types.Operator):
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)
+ ret.append("op.%s" % prop_id)
return ret
@@ -351,14 +350,3 @@ class WM_MT_operator_presets(bpy.types.Menu):
return AddPresetOperator.operator_path(self.operator)
preset_operator = "script.execute_preset"
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
index 579af9b34b3..64af25e7b0f 100644
--- a/release/scripts/op/screen_play_rendered_anim.py
+++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py
@@ -29,19 +29,14 @@ import os
def guess_player_path(preset):
- import platform
- try:
- system = platform.system()
- except UnicodeDecodeError:
- import sys
- system = sys.platform
+ import sys
if preset == 'BLENDER24':
player_path = "blender"
- if system == 'Darwin':
+ if sys.platform == "darwin":
test_path = "/Applications/blender 2.49.app/Contents/MacOS/blender"
- elif system in ('Windows', 'win32'):
+ elif sys.platform[:3] == "win":
test_path = "/Program Files/Blender Foundation/Blender/blender.exe"
if os.path.exists(test_path):
@@ -50,7 +45,8 @@ def guess_player_path(preset):
elif preset == 'DJV':
player_path = "djv_view"
- if system == 'Darwin':
+ 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'
if os.path.exists(test_path):
player_path = test_path
@@ -144,14 +140,3 @@ class PlayRenderedAnim(bpy.types.Operator):
#raise OSError("Couldn't find an external animation player.")
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py
index de341bef269..16b72406c49 100644
--- a/release/scripts/op/sequencer.py
+++ b/release/scripts/startup/bl_operators/sequencer.py
@@ -20,7 +20,7 @@
import bpy
-from bpy.props import *
+from bpy.props import IntProperty
class SequencerCrossfadeSounds(bpy.types.Operator):
@@ -132,15 +132,3 @@ class SequencerDeinterlaceSelectedMovies(bpy.types.Operator):
s.use_deinterlace = True
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
index 12895fae1c5..ad5ec15ff80 100644
--- a/release/scripts/op/uvcalc_follow_active.py
+++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py
@@ -245,20 +245,6 @@ class FollowActiveQuads(bpy.types.Operator):
main(context, self)
return {'FINISHED'}
-
-# Add to a menu
-menu_func = (lambda self, context: self.layout.operator(FollowActiveQuads.bl_idname))
-
-
-def register():
- bpy.utils.register_module(__name__)
- bpy.types.VIEW3D_MT_uv_map.append(menu_func)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.VIEW3D_MT_uv_map.remove(menu_func)
-
-
-if __name__ == "__main__":
- register()
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self)
diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
new file mode 100644
index 00000000000..3893612437a
--- /dev/null
+++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py
@@ -0,0 +1,582 @@
+# ##### 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
+import mathutils
+
+
+class prettyface(object):
+ __slots__ = "uv", "width", "height", "children", "xoff", "yoff", "has_parent", "rot"
+
+ def __init__(self, data):
+ self.has_parent = False
+ self.rot = False # only used for triables
+ self.xoff = 0
+ self.yoff = 0
+
+ if type(data) == list: # list of data
+ self.uv = None
+
+ # join the data
+ if len(data) == 2:
+ # 2 vertical blocks
+ data[1].xoff = data[0].width
+ self.width = data[0].width * 2
+ self.height = data[0].height
+
+ elif len(data) == 4:
+ # 4 blocks all the same size
+ d = data[0].width # dimension x/y are the same
+
+ data[1].xoff += d
+ data[2].yoff += d
+
+ data[3].xoff += d
+ data[3].yoff += d
+
+ self.width = self.height = d * 2
+
+ #else:
+ # print(len(data), data)
+ # raise "Error"
+
+ for pf in data:
+ pf.has_parent = True
+
+ self.children = data
+
+ elif type(data) == tuple:
+ # 2 blender faces
+ # f, (len_min, len_mid, len_max)
+ self.uv = data
+
+ f1, lens1, lens1ord = data[0]
+ if data[1]:
+ f2, lens2, lens2ord = data[1]
+ self.width = (lens1[lens1ord[0]] + lens2[lens2ord[0]]) / 2.0
+ self.height = (lens1[lens1ord[1]] + lens2[lens2ord[1]]) / 2.0
+ else: # 1 tri :/
+ self.width = lens1[0]
+ self.height = lens1[1]
+
+ self.children = []
+
+ else: # blender face
+ # self.uv = data.uv
+ self.uv = data.id_data.uv_textures.active.data[data.index].uv # XXX25
+
+ # cos = [v.co for v in data]
+ cos = [data.id_data.vertices[v].co for v in data.vertices] # XXX25
+
+ self.width = ((cos[0] - cos[1]).length + (cos[2] - cos[3]).length) / 2.0
+ self.height = ((cos[1] - cos[2]).length + (cos[0] - cos[3]).length) / 2.0
+
+ self.children = []
+
+ def spin(self):
+ if self.uv and len(self.uv) == 4:
+ self.uv = self.uv[1], self.uv[2], self.uv[3], self.uv[0]
+
+ self.width, self.height = self.height, self.width
+ self.xoff, self.yoff = self.yoff, self.xoff # not needed?
+ self.rot = not self.rot # only for tri pairs.
+ # print("spinning")
+ for pf in self.children:
+ pf.spin()
+
+ def place(self, xoff, yoff, xfac, yfac, margin_w, margin_h):
+ from math import pi
+
+ xoff += self.xoff
+ yoff += self.yoff
+
+ for pf in self.children:
+ pf.place(xoff, yoff, xfac, yfac, margin_w, margin_h)
+
+ uv = self.uv
+ if not uv:
+ return
+
+ x1 = xoff
+ y1 = yoff
+ x2 = xoff + self.width
+ y2 = yoff + self.height
+
+ # Scale the values
+ x1 = x1 / xfac + margin_w
+ x2 = x2 / xfac - margin_w
+ y1 = y1 / yfac + margin_h
+ y2 = y2 / yfac - margin_h
+
+ # 2 Tri pairs
+ if len(uv) == 2:
+ # match the order of angle sizes of the 3d verts with the UV angles and rotate.
+ def get_tri_angles(v1, v2, v3):
+ a1 = (v2 - v1).angle(v3 - v1, pi)
+ a2 = (v1 - v2).angle(v3 - v2, pi)
+ a3 = pi - (a1 + a2) # a3= (v2 - v3).angle(v1 - v3)
+
+ return [(a1, 0), (a2, 1), (a3, 2)]
+
+ def set_uv(f, p1, p2, p3):
+
+ # cos =
+ #v1 = cos[0]-cos[1]
+ #v2 = cos[1]-cos[2]
+ #v3 = cos[2]-cos[0]
+
+ # angles_co = get_tri_angles(*[v.co for v in f])
+ angles_co = get_tri_angles(*[f.id_data.vertices[v].co for v in f.vertices]) # XXX25
+
+ angles_co.sort()
+ I = [i for a, i in angles_co]
+
+ # fuv = f.uv
+ fuv = f.id_data.uv_textures.active.data[f.index].uv # XXX25
+
+ if self.rot:
+ fuv[I[2]] = p1
+ fuv[I[1]] = p2
+ fuv[I[0]] = p3
+ else:
+ fuv[I[2]] = p1
+ fuv[I[0]] = p2
+ fuv[I[1]] = p3
+
+ f, lens, lensord = uv[0]
+
+ set_uv(f, (x1, y1), (x1, y2 - margin_h), (x2 - margin_w, y1))
+
+ if uv[1]:
+ f, lens, lensord = uv[1]
+ set_uv(f, (x2, y2), (x2, y1 + margin_h), (x1 + margin_w, y2))
+
+ else: # 1 QUAD
+ uv[1][0], uv[1][1] = x1, y1
+ uv[2][0], uv[2][1] = x1, y2
+ uv[3][0], uv[3][1] = x2, y2
+ uv[0][0], uv[0][1] = x2, y1
+
+ def __hash__(self):
+ # None unique hash
+ return self.width, self.height
+
+
+def lightmap_uvpack(meshes,
+ PREF_SEL_ONLY=True,
+ PREF_NEW_UVLAYER=False,
+ PREF_PACK_IN_ONE=False,
+ PREF_APPLY_IMAGE=False,
+ PREF_IMG_PX_SIZE=512,
+ PREF_BOX_DIV=8,
+ PREF_MARGIN_DIV=512
+ ):
+ '''
+ BOX_DIV if the maximum division of the UV map that
+ a box may be consolidated into.
+ Basicly, a lower value will be slower but waist less space
+ and a higher value will have more clumpy boxes but more waisted space
+ '''
+ import time
+ from math import sqrt
+
+ if not meshes:
+ return
+
+ t = time.time()
+
+ if PREF_PACK_IN_ONE:
+ if PREF_APPLY_IMAGE:
+ image = bpy.data.images.new(name="lightmap", width=PREF_IMG_PX_SIZE, height=PREF_IMG_PX_SIZE, alpha=False)
+ face_groups = [[]]
+ else:
+ face_groups = []
+
+ for me in meshes:
+ # Add face UV if it does not exist.
+ # All new faces are selected.
+ if not me.uv_textures:
+ me.uv_textures.new()
+
+ if PREF_SEL_ONLY:
+ faces = [f for f in me.faces if f.select]
+ else:
+ faces = me.faces[:]
+
+ if PREF_PACK_IN_ONE:
+ face_groups[0].extend(faces)
+ else:
+ face_groups.append(faces)
+
+ if PREF_NEW_UVLAYER:
+ me.uv_textures.new()
+
+ for face_sel in face_groups:
+ print("\nStarting unwrap")
+
+ if len(face_sel) < 4:
+ print("\tWarning, less then 4 faces, skipping")
+ continue
+
+ pretty_faces = [prettyface(f) for f in face_sel if len(f.vertices) == 4]
+
+ # Do we have any tri's
+ if len(pretty_faces) != len(face_sel):
+
+ # Now add tri's, not so simple because we need to pair them up.
+ def trylens(f):
+ # f must be a tri
+
+ # cos = [v.co for v in f]
+ cos = [f.id_data.vertices[v].co for v in f.vertices] # XXX25
+
+ lens = [(cos[0] - cos[1]).length, (cos[1] - cos[2]).length, (cos[2] - cos[0]).length]
+
+ lens_min = lens.index(min(lens))
+ lens_max = lens.index(max(lens))
+ for i in range(3):
+ if i != lens_min and i != lens_max:
+ lens_mid = i
+ break
+ lens_order = lens_min, lens_mid, lens_max
+
+ return f, lens, lens_order
+
+ tri_lengths = [trylens(f) for f in face_sel if len(f.vertices) == 3]
+ del trylens
+
+ def trilensdiff(t1, t2):
+ return\
+ abs(t1[1][t1[2][0]] - t2[1][t2[2][0]]) + \
+ abs(t1[1][t1[2][1]] - t2[1][t2[2][1]]) + \
+ abs(t1[1][t1[2][2]] - t2[1][t2[2][2]])
+
+ while tri_lengths:
+ tri1 = tri_lengths.pop()
+
+ if not tri_lengths:
+ pretty_faces.append(prettyface((tri1, None)))
+ break
+
+ best_tri_index = -1
+ best_tri_diff = 100000000.0
+
+ for i, tri2 in enumerate(tri_lengths):
+ diff = trilensdiff(tri1, tri2)
+ if diff < best_tri_diff:
+ best_tri_index = i
+ best_tri_diff = diff
+
+ pretty_faces.append(prettyface((tri1, tri_lengths.pop(best_tri_index))))
+
+ # Get the min, max and total areas
+ max_area = 0.0
+ min_area = 100000000.0
+ tot_area = 0
+ for f in face_sel:
+ area = f.area
+ if area > max_area:
+ max_area = area
+ if area < min_area:
+ min_area = area
+ tot_area += area
+
+ max_len = sqrt(max_area)
+ min_len = sqrt(min_area)
+ side_len = sqrt(tot_area)
+
+ # Build widths
+
+ curr_len = max_len
+
+ print("\tGenerating lengths...", end="")
+
+ lengths = []
+ while curr_len > min_len:
+ lengths.append(curr_len)
+ curr_len = curr_len / 2.0
+
+ # Dont allow boxes smaller then the margin
+ # since we contract on the margin, boxes that are smaller will create errors
+ # print(curr_len, side_len/MARGIN_DIV)
+ if curr_len / 4.0 < side_len / PREF_MARGIN_DIV:
+ break
+
+ if not lengths:
+ lengths.append(curr_len)
+
+ # convert into ints
+ lengths_to_ints = {}
+
+ l_int = 1
+ for l in reversed(lengths):
+ lengths_to_ints[l] = l_int
+ l_int *= 2
+
+ lengths_to_ints = list(lengths_to_ints.items())
+ lengths_to_ints.sort()
+ print("done")
+
+ # apply quantized values.
+
+ for pf in pretty_faces:
+ w = pf.width
+ h = pf.height
+ bestw_diff = 1000000000.0
+ besth_diff = 1000000000.0
+ new_w = 0.0
+ new_h = 0.0
+ for l, i in lengths_to_ints:
+ d = abs(l - w)
+ if d < bestw_diff:
+ bestw_diff = d
+ new_w = i # assign the int version
+
+ d = abs(l - h)
+ if d < besth_diff:
+ besth_diff = d
+ new_h = i # ditto
+
+ pf.width = new_w
+ pf.height = new_h
+
+ if new_w > new_h:
+ pf.spin()
+
+ print("...done")
+
+ # Since the boxes are sized in powers of 2, we can neatly group them into bigger squares
+ # this is done hierarchily, so that we may avoid running the pack function
+ # on many thousands of boxes, (under 1k is best) because it would get slow.
+ # Using an off and even dict us usefull because they are packed differently
+ # where w/h are the same, their packed in groups of 4
+ # where they are different they are packed in pairs
+ #
+ # After this is done an external pack func is done that packs the whole group.
+
+ print("\tConsolidating Boxes...", end="")
+ even_dict = {} # w/h are the same, the key is an int (w)
+ odd_dict = {} # w/h are different, the key is the (w,h)
+
+ for pf in pretty_faces:
+ w, h = pf.width, pf.height
+ if w == h:
+ even_dict.setdefault(w, []).append(pf)
+ else:
+ odd_dict.setdefault((w, h), []).append(pf)
+
+ # Count the number of boxes consolidated, only used for stats.
+ c = 0
+
+ # This is tricky. the total area of all packed boxes, then squt that to get an estimated size
+ # this is used then converted into out INT space so we can compare it with
+ # the ints assigned to the boxes size
+ # and divided by BOX_DIV, basicly if BOX_DIV is 8
+ # ...then the maximum box consolidataion (recursive grouping) will have a max width & height
+ # ...1/8th of the UV size.
+ # ...limiting this is needed or you end up with bug unused texture spaces
+ # ...however if its too high, boxpacking is way too slow for high poly meshes.
+ float_to_int_factor = lengths_to_ints[0][0]
+ if float_to_int_factor > 0:
+ max_int_dimension = int(((side_len / float_to_int_factor)) / PREF_BOX_DIV)
+ ok = True
+ else:
+ max_int_dimension = 0.0 # wont be used
+ ok = False
+
+ # RECURSIVE prettyface grouping
+ while ok:
+ ok = False
+
+ # Tall boxes in groups of 2
+ for d, boxes in odd_dict.items():
+ if d[1] < max_int_dimension:
+ #\boxes.sort(key = lambda a: len(a.children))
+ while len(boxes) >= 2:
+ # print("foo", len(boxes))
+ ok = True
+ c += 1
+ pf_parent = prettyface([boxes.pop(), boxes.pop()])
+ pretty_faces.append(pf_parent)
+
+ w, h = pf_parent.width, pf_parent.height
+
+ if w > h:
+ raise "error"
+
+ if w == h:
+ even_dict.setdefault(w, []).append(pf_parent)
+ else:
+ odd_dict.setdefault((w, h), []).append(pf_parent)
+
+ # Even boxes in groups of 4
+ for d, boxes in even_dict.items():
+ if d < max_int_dimension:
+ boxes.sort(key=lambda a: len(a.children))
+
+ while len(boxes) >= 4:
+ # print("bar", len(boxes))
+ ok = True
+ c += 1
+
+ pf_parent = prettyface([boxes.pop(), boxes.pop(), boxes.pop(), boxes.pop()])
+ pretty_faces.append(pf_parent)
+ w = pf_parent.width # width and weight are the same
+ even_dict.setdefault(w, []).append(pf_parent)
+
+ del even_dict
+ del odd_dict
+
+ orig = len(pretty_faces)
+
+ pretty_faces = [pf for pf in pretty_faces if not pf.has_parent]
+
+ # spin every second prettyface
+ # if there all vertical you get less efficiently used texture space
+ i = len(pretty_faces)
+ d = 0
+ while i:
+ i -= 1
+ pf = pretty_faces[i]
+ if pf.width != pf.height:
+ d += 1
+ if d % 2: # only pack every second
+ pf.spin()
+ # pass
+
+ print("Consolidated", c, "boxes, done")
+ # print("done", orig, len(pretty_faces))
+
+ # boxes2Pack.append([islandIdx, w,h])
+ print("\tPacking Boxes", len(pretty_faces), end="...")
+ boxes2Pack = [[0.0, 0.0, pf.width, pf.height, i] for i, pf in enumerate(pretty_faces)]
+ packWidth, packHeight = mathutils.geometry.box_pack_2d(boxes2Pack)
+
+ # print(packWidth, packHeight)
+
+ packWidth = float(packWidth)
+ packHeight = float(packHeight)
+
+ margin_w = ((packWidth) / PREF_MARGIN_DIV) / packWidth
+ margin_h = ((packHeight) / PREF_MARGIN_DIV) / packHeight
+
+ # print(margin_w, margin_h)
+ print("done")
+
+ # Apply the boxes back to the UV coords.
+ print("\twriting back UVs", end="")
+ for i, box in enumerate(boxes2Pack):
+ pretty_faces[i].place(box[0], box[1], packWidth, packHeight, margin_w, margin_h)
+ # pf.place(box[1][1], box[1][2], packWidth, packHeight, margin_w, margin_h)
+ print("done")
+
+ if PREF_APPLY_IMAGE:
+ if not PREF_PACK_IN_ONE:
+ image = Image.New("lightmap", PREF_IMG_PX_SIZE, PREF_IMG_PX_SIZE, 24)
+
+ for f in face_sel:
+ # f.image = image
+ f.id_data.uv_textures.active.data[f.index].image = image # XXX25
+
+ for me in meshes:
+ me.update()
+
+ print("finished all %.2f " % (time.time() - t))
+
+ # Window.RedrawAll()
+
+
+def unwrap(operator, context, **kwargs):
+
+ is_editmode = (bpy.context.object.mode == 'EDIT')
+ if is_editmode:
+ bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
+
+ PREF_ACT_ONLY = kwargs.pop("PREF_ACT_ONLY")
+
+ meshes = []
+ if PREF_ACT_ONLY:
+ obj = context.scene.objects.active
+ if obj and obj.type == 'MESH':
+ meshes = [obj.data]
+ else:
+ meshes = {me.name: me for obj in context.selected_objects if obj.type == 'MESH' for me in (obj.data,) if not me.library if len(me.faces)}.values()
+
+ if not meshes:
+ operator.report({'ERROR'}, "No mesh object.")
+ return {'CANCELLED'}
+
+ lightmap_uvpack(meshes, **kwargs)
+
+ if is_editmode:
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+
+ return {'FINISHED'}
+
+from bpy.props import BoolProperty, FloatProperty, IntProperty, EnumProperty
+
+
+class LightMapPack(bpy.types.Operator):
+ '''Follow UVs from active quads along continuous face loops'''
+ bl_idname = "uv.lightmap_pack"
+ bl_label = "Lightmap Pack"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ PREF_CONTEXT = bpy.props.EnumProperty(
+ items=(("SEL_FACES", "Selected Faces", "Space all UVs evently"),
+ ("ALL_FACES", "All Faces", "Average space UVs edge length of each loop"),
+ ("ALL_OBJECTS", "Selected Mesh Object", "Average space UVs edge length of each loop")
+ ),
+ name="Selection",
+ description="")
+
+ # Image & UVs...
+ PREF_PACK_IN_ONE = BoolProperty(name="Share Tex Space", default=True, description="Objects Share texture space, map all objects into 1 uvmap")
+ PREF_NEW_UVLAYER = BoolProperty(name="New UV Layer", default=False, description="Create a new UV layer for every mesh packed")
+ PREF_APPLY_IMAGE = BoolProperty(name="New Image", default=False, description="Assign new images for every mesh (only one if shared tex space enabled)")
+ PREF_IMG_PX_SIZE = IntProperty(name="Image Size", min=64, max=5000, default=512, description="Width and Height for the new image")
+
+ # UV Packing...
+ PREF_BOX_DIV = IntProperty(name="Pack Quality", min=1, max=48, default=12, description="Pre Packing before the complex boxpack")
+ PREF_MARGIN_DIV = FloatProperty(name="Margin", min=0.001, max=1.0, default=0.1, description="Size of the margin as a division of the UV")
+
+ def execute(self, context):
+ kwargs = self.as_keywords()
+ PREF_CONTEXT = kwargs.pop("PREF_CONTEXT")
+
+ if PREF_CONTEXT == 'SEL_FACES':
+ kwargs["PREF_ACT_ONLY"] = True
+ kwargs["PREF_SEL_ONLY"] = True
+ elif PREF_CONTEXT == 'ALL_FACES':
+ kwargs["PREF_ACT_ONLY"] = True
+ kwargs["PREF_SEL_ONLY"] = False
+ elif PREF_CONTEXT == 'ALL_OBJECTS':
+ kwargs["PREF_ACT_ONLY"] = False
+ kwargs["PREF_SEL_ONLY"] = False
+ else:
+ raise Exception("invalid context")
+
+ kwargs["PREF_MARGIN_DIV"] = int(1.0 / (kwargs["PREF_MARGIN_DIV"] / 100.0))
+
+ return unwrap(self, context, **kwargs)
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self)
diff --git a/release/scripts/op/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
index 8cdf593f98d..4f5b1d8b233 100644
--- a/release/scripts/op/uvcalc_smart_project.py
+++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py
@@ -1,31 +1,25 @@
-# --------------------------------------------------------------------------
-# Smart Projection UV Projection Unwrapper v1.2 by Campbell Barton (AKA Ideasman)
-# --------------------------------------------------------------------------
-# ***** BEGIN GPL LICENSE BLOCK *****
+# ##### 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 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>
from mathutils import Matrix, Vector, geometry
-import time
import bpy
-from math import cos, radians
DEG_TO_RAD = 0.017453292519943295 # pi/180.0
SMALL_NUM = 0.000000001
@@ -36,14 +30,10 @@ global USER_FILL_HOLES_QUALITY
USER_FILL_HOLES = None
USER_FILL_HOLES_QUALITY = None
-dict_matrix = {}
-
def pointInTri2D(v, v1, v2, v3):
- global dict_matrix
-
key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
- # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face.
+ # Commented because its slower to do the bounds check, we should realy cache the bounds info for each face.
'''
# BOUNDS CHECK
xmin= 1000000
@@ -268,21 +258,6 @@ def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1):
h = maxy-miny
return (w*h, w,h), vecs # Area, vecs
-# Takes a list of faces that make up a UV island and rotate
-# until they optimally fit inside a square.
-ROTMAT_2D_POS_90D = Matrix.Rotation( radians(90.0), 2)
-ROTMAT_2D_POS_45D = Matrix.Rotation( radians(45.0), 2)
-
-RotMatStepRotation = []
-rot_angle = 22.5 #45.0/2
-while rot_angle > 0.1:
- RotMatStepRotation.append([\
- Matrix.Rotation( radians(rot_angle), 2),\
- Matrix.Rotation( radians(-rot_angle), 2)])
-
- rot_angle = rot_angle/2.0
-
-
def optiRotateUvIsland(faces):
global currentArea
@@ -464,7 +439,7 @@ def mergeUvIslands(islandList):
# if targetIsland[3] > (sourceIsland[2]) and\ #
- # print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass'
+ # print USER_FREE_SPACE_TO_TEST_QUALITY
if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
targetIsland[4] > sourceIsland[4] and\
targetIsland[5] > sourceIsland[5]:
@@ -734,7 +709,7 @@ def packIslands(islandList):
#print '\tPacking UV Islands...'
#XXX Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes) )
- time1 = time.time()
+ # time1 = time.time()
packWidth, packHeight = geometry.box_pack_2d(packBoxes)
# print 'Box Packing Time:', time.time() - time1
@@ -793,6 +768,27 @@ class thickface(object):
self.area = face.area
self.edge_keys = face.edge_keys
+
+def main_consts():
+ from math import radians
+
+ global ROTMAT_2D_POS_90D
+ global ROTMAT_2D_POS_45D
+ global RotMatStepRotation
+
+ ROTMAT_2D_POS_90D = Matrix.Rotation( radians(90.0), 2)
+ ROTMAT_2D_POS_45D = Matrix.Rotation( radians(45.0), 2)
+
+ RotMatStepRotation = []
+ rot_angle = 22.5 #45.0/2
+ while rot_angle > 0.1:
+ RotMatStepRotation.append([\
+ Matrix.Rotation( radians(rot_angle), 2),\
+ Matrix.Rotation( radians(-rot_angle), 2)])
+
+ rot_angle = rot_angle/2.0
+
+
global ob
ob = None
def main(context, island_margin, projection_limit):
@@ -800,6 +796,21 @@ def main(context, island_margin, projection_limit):
global USER_FILL_HOLES_QUALITY
global USER_STRETCH_ASPECT
global USER_ISLAND_MARGIN
+
+ from math import cos
+ import time
+
+ global dict_matrix
+ dict_matrix = {}
+
+
+ # Constants:
+ # Takes a list of faces that make up a UV island and rotate
+ # until they optimally fit inside a square.
+ global ROTMAT_2D_POS_90D
+ global ROTMAT_2D_POS_45D
+ global RotMatStepRotation
+ main_consts()
#XXX objects= bpy.data.scenes.active.objects
objects = context.selected_editable_objects
@@ -868,7 +879,7 @@ def main(context, island_margin, projection_limit):
time1 = time.time()
- # Tag as False se we dont operate on teh same mesh twice.
+ # Tag as False se we dont operate on the same mesh twice.
#XXX bpy.data.meshes.tag = False
for me in bpy.data.meshes:
me.tag = False
@@ -1074,6 +1085,8 @@ def main(context, island_margin, projection_limit):
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT')
+ dict_matrix.clear()
+
#XXX Window.DrawProgressBar(1.0, "")
#XXX Window.WaitCursor(0)
#XXX Window.RedrawAll()
@@ -1098,7 +1111,7 @@ def main(context, island_margin, projection_limit):
]
"""
-from bpy.props import *
+from bpy.props import FloatProperty
class SmartProject(bpy.types.Operator):
@@ -1123,20 +1136,6 @@ class SmartProject(bpy.types.Operator):
main(context, self.island_margin, self.angle_limit)
return {'FINISHED'}
-
-# Add to a menu
-menu_func = (lambda self, context: self.layout.operator(SmartProject.bl_idname,
- text="Smart Project"))
-
-
-def register():
- bpy.utils.register_module(__name__)
- bpy.types.VIEW3D_MT_uv_map.append(menu_func)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.VIEW3D_MT_uv_map.remove(menu_func)
-
-if __name__ == "__main__":
- register()
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_props_dialog(self)
diff --git a/release/scripts/op/vertexpaint_dirt.py b/release/scripts/startup/bl_operators/vertexpaint_dirt.py
index d5060e913ae..672db71e361 100644
--- a/release/scripts/op/vertexpaint_dirt.py
+++ b/release/scripts/startup/bl_operators/vertexpaint_dirt.py
@@ -30,16 +30,10 @@
# but results are far more accurate
#
-import bpy
-import math
-import time
-
-from mathutils import Vector
-from bpy.props import *
-
def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
-## Window.WaitCursor(1)
+ from mathutils import Vector
+ from math import acos
#BPyMesh.meshCalcNormals(me)
@@ -76,7 +70,7 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean,
vec /= tot_con
# angle is the acos of the dot product between vert and connected verts normals
- ang = math.acos(no.dot(vec))
+ ang = acos(no.dot(vec))
# enforce min/max
ang = max(clamp_dirt, ang)
@@ -146,11 +140,12 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean,
col[1] = tone * col[1]
col[2] = tone * col[2]
-## Window.WaitCursor(0)
+import bpy
+from bpy.props import FloatProperty, IntProperty, BoolProperty
-class VertexPaintDirt(bpy.types.Operator):
+class VertexPaintDirt(bpy.types.Operator):
bl_idname = "paint.vertex_color_dirt"
bl_label = "Dirty Vertex Colors"
bl_options = {'REGISTER', 'UNDO'}
@@ -162,29 +157,20 @@ class VertexPaintDirt(bpy.types.Operator):
dirt_only = BoolProperty(name="Dirt Only", description="Dont calculate cleans for convex areas", default=False)
def execute(self, context):
+ import time
+ from math import radians
obj = context.object
if not obj or obj.type != 'MESH':
- print('Error, no active mesh object, aborting')
- return('CANCELLED',)
+ self.report({'ERROR'}, "Error, no active mesh object, aborting")
+ return {'CANCELLED'}
mesh = obj.data
t = time.time()
- applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, math.radians(self.dirt_angle), math.radians(self.clean_angle), self.dirt_only)
+ applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, radians(self.dirt_angle), radians(self.clean_angle), self.dirt_only)
print('Dirt calculated in %.6f' % (time.time() - t))
return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/op/wm.py b/release/scripts/startup/bl_operators/wm.py
index cbc9e3cd55f..53c8d562297 100644
--- a/release/scripts/op/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -19,8 +19,7 @@
# <pep8 compliant>
import bpy
-
-from bpy.props import *
+from bpy.props import StringProperty, BoolProperty, IntProperty, FloatProperty
from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear
@@ -51,7 +50,7 @@ rna_relative_prop = BoolProperty(name="Relative",
def context_path_validate(context, data_path):
import sys
try:
- value = eval("context.%s" % data_path)
+ value = eval("context.%s" % data_path) if data_path else Ellipsis
except AttributeError:
if "'NoneType'" in str(sys.exc_info()[1]):
# One of the items in the rna path is None, just ignore this
@@ -75,20 +74,20 @@ def execute_context_assign(self, context):
return {'FINISHED'}
-class BRUSH_OT_set_active_number(bpy.types.Operator):
+class BRUSH_OT_active_index_set(bpy.types.Operator):
'''Set active sculpt/paint brush from it's number'''
- bl_idname = "brush.set_active_number"
+ bl_idname = "brush.active_index_set"
bl_label = "Set Brush Number"
mode = StringProperty(name="mode",
description="Paint mode to set brush for", maxlen=1024)
- number = IntProperty(name="number",
+ index = IntProperty(name="number",
description="Brush number")
_attr_dict = {"sculpt": "use_paint_sculpt",
"vertex_paint": "use_paint_vertex",
"weight_paint": "use_paint_weight",
- "image_paint": "use_paint_texture"}
+ "image_paint": "use_paint_image"}
def execute(self, context):
attr = self._attr_dict.get(self.mode)
@@ -96,7 +95,7 @@ class BRUSH_OT_set_active_number(bpy.types.Operator):
return {'CANCELLED'}
for i, brush in enumerate((cur for cur in bpy.data.brushes if getattr(cur, attr))):
- if i == self.number:
+ if i == self.index:
getattr(context.tool_settings, self.mode).brush = brush
return {'FINISHED'}
@@ -336,7 +335,7 @@ class WM_OT_context_cycle_enum(bpy.types.Operator):
if type(rna_prop) != bpy.types.EnumProperty:
raise Exception("expected an enum property")
- enums = rna_struct.properties[rna_prop_str].items.keys()
+ enums = rna_struct.properties[rna_prop_str].enum_items.keys()
orig_index = enums.index(orig_value)
# Have the info we need, advance to the next item
@@ -384,6 +383,39 @@ class WM_OT_context_cycle_array(bpy.types.Operator):
return {'FINISHED'}
+class WM_MT_context_menu_enum(bpy.types.Menu):
+ bl_label = ""
+ data_path = "" # BAD DESIGN, set from operator below.
+
+ def draw(self, context):
+ data_path = self.data_path
+ value = context_path_validate(bpy.context, data_path)
+ if value is Ellipsis:
+ return {'PASS_THROUGH'}
+ base_path, prop_string = data_path.rsplit(".", 1)
+ value_base = context_path_validate(context, base_path)
+
+ values = [(i.name, i.identifier) for i in value_base.bl_rna.properties[prop_string].enum_items]
+
+ for name, identifier in values:
+ prop = self.layout.operator("wm.context_set_enum", text=name)
+ prop.data_path = data_path
+ prop.value = identifier
+
+
+class WM_OT_context_menu_enum(bpy.types.Operator):
+ bl_idname = "wm.context_menu_enum"
+ bl_label = "Context Enum Menu"
+ bl_options = {'UNDO'}
+ data_path = rna_path_prop
+
+ def execute(self, context):
+ data_path = self.data_path
+ WM_MT_context_menu_enum.data_path = data_path
+ bpy.ops.wm.call_menu(name="WM_MT_context_menu_enum")
+ return {'PASS_THROUGH'}
+
+
class WM_OT_context_set_id(bpy.types.Operator):
'''Toggle a context value.'''
bl_idname = "wm.context_set_id"
@@ -520,6 +552,7 @@ class WM_OT_url_open(bpy.types.Operator):
def execute(self, context):
import webbrowser
+ _webbrowser_bug_fix()
webbrowser.open(self.url)
return {'FINISHED'}
@@ -543,7 +576,7 @@ class WM_OT_path_open(bpy.types.Operator):
self.report({'ERROR'}, "File '%s' not found" % filepath)
return {'CANCELLED'}
- if sys.platform == 'win32':
+ if sys.platform[:3] == "win":
subprocess.Popen(['start', filepath], shell=True)
elif sys.platform == 'darwin':
subprocess.Popen(['open', filepath])
@@ -563,7 +596,10 @@ class WM_OT_doc_view(bpy.types.Operator):
bl_label = "View Documentation"
doc_id = doc_id
- _prefix = "http://www.blender.org/documentation/blender_python_api_%s" % "_".join(str(v) for v in bpy.app.version)
+ if bpy.app.version_cycle == "release":
+ _prefix = "http://www.blender.org/documentation/blender_python_api_%s%s_release" % ("_".join(str(v) for v in bpy.app.version[:2]), bpy.app.version_char)
+ else:
+ _prefix = "http://www.blender.org/documentation/blender_python_api_%s" % "_".join(str(v) for v in bpy.app.version)
def _nested_class_string(self, class_string):
ls = []
@@ -584,6 +620,15 @@ class WM_OT_doc_view(bpy.types.Operator):
url = '%s/bpy.ops.%s.html#bpy.ops.%s.%s' % \
(self._prefix, class_name, class_name, class_prop)
else:
+
+ # detect if this is a inherited member and use that name instead
+ rna_parent = getattr(bpy.types, class_name).bl_rna
+ rna_prop = rna_parent.properties[class_prop]
+ rna_parent = rna_parent.base
+ while rna_parent and rna_prop == rna_parent.properties.get(class_prop):
+ class_name = rna_parent.identifier
+ rna_parent = rna_parent.base
+
# It so happens that epydoc nests these, not sphinx
# class_name_full = self._nested_class_string(class_name)
url = '%s/bpy.types.%s.html#bpy.types.%s.%s' % \
@@ -593,6 +638,7 @@ class WM_OT_doc_view(bpy.types.Operator):
return {'PASS_THROUGH'}
import webbrowser
+ _webbrowser_bug_fix()
webbrowser.open(url)
return {'FINISHED'}
@@ -669,9 +715,6 @@ class WM_OT_doc_edit(bpy.types.Operator):
return wm.invoke_props_dialog(self, width=600)
-from bpy.props import *
-
-
rna_path = StringProperty(name="Property Edit",
description="Property data_path edit", maxlen=1024, default="", options={'HIDDEN'})
@@ -702,7 +745,12 @@ class WM_OT_properties_edit(bpy.types.Operator):
data_path = self.data_path
value = self.value
prop = self.property
- prop_old = self._last_prop[0]
+
+ prop_old = getattr(self, "_last_prop", [None])[0]
+
+ if prop_old is None:
+ self.report({'ERROR'}, "Direct execution not supported")
+ return {'CANCELLED'}
try:
value_eval = eval(value)
@@ -734,10 +782,21 @@ class WM_OT_properties_edit(bpy.types.Operator):
prop_ui['description'] = self.description
+ # otherwise existing buttons which reference freed
+ # memory may crash blender [#26510]
+ # context.area.tag_redraw()
+ for win in context.window_manager.windows:
+ for area in win.screen.areas:
+ area.tag_redraw()
+
return {'FINISHED'}
def invoke(self, context, event):
+ if not self.data_path:
+ self.report({'ERROR'}, "Data path not set")
+ return {'CANCELLED'}
+
self._last_prop = [self.property]
item = eval("context.%s" % self.data_path)
@@ -779,6 +838,18 @@ class WM_OT_properties_add(bpy.types.Operator):
return {'FINISHED'}
+class WM_OT_properties_context_change(bpy.types.Operator):
+ "Change the context tab in a Properties Window"
+ bl_idname = "wm.properties_context_change"
+ bl_label = ""
+
+ context = StringProperty(name="Context", maxlen=32)
+
+ def execute(self, context):
+ context.space_data.context = (self.context)
+ return {'FINISHED'}
+
+
class WM_OT_properties_remove(bpy.types.Operator):
'''Internal use (edit a property data_path)'''
bl_idname = "wm.properties_remove"
@@ -804,6 +875,41 @@ class WM_OT_keyconfig_activate(bpy.types.Operator):
return {'FINISHED'}
+class WM_OT_appconfig_default(bpy.types.Operator):
+ bl_idname = "wm.appconfig_default"
+ bl_label = "Default Application Configuration"
+
+ def execute(self, context):
+ import os
+
+ context.window_manager.keyconfigs.active = context.window_manager.keyconfigs.default
+
+ filepath = os.path.join(bpy.utils.preset_paths("interaction")[0], "blender.py")
+
+ if os.path.exists(filepath):
+ bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
+
+ return {'FINISHED'}
+
+
+class WM_OT_appconfig_activate(bpy.types.Operator):
+ bl_idname = "wm.appconfig_activate"
+ bl_label = "Activate Application Configuration"
+
+ filepath = StringProperty(name="File Path", maxlen=1024)
+
+ def execute(self, context):
+ import os
+ bpy.utils.keyconfig_set(self.filepath)
+
+ filepath = self.filepath.replace("keyconfig", "interaction")
+
+ if os.path.exists(filepath):
+ bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
+
+ return {'FINISHED'}
+
+
class WM_OT_sysinfo(bpy.types.Operator):
'''Generate System Info'''
bl_idname = "wm.sysinfo"
@@ -815,12 +921,92 @@ class WM_OT_sysinfo(bpy.types.Operator):
return {'FINISHED'}
-def register():
- bpy.utils.register_module(__name__)
+class WM_OT_copy_prev_settings(bpy.types.Operator):
+ '''Copy settings from previous version'''
+ bl_idname = "wm.copy_prev_settings"
+ bl_label = "Copy Previous Settings"
+
+ def execute(self, context):
+ import os
+ import shutil
+ ver = bpy.app.version
+ ver_old = ((ver[0] * 100) + ver[1]) - 1
+ path_src = bpy.utils.resource_path('USER', ver_old // 100, ver_old % 100)
+ path_dst = bpy.utils.resource_path('USER')
+
+ if os.path.isdir(path_dst):
+ self.report({'ERROR'}, "Target path %r exists" % path_dst)
+ elif not os.path.isdir(path_src):
+ self.report({'ERROR'}, "Source path %r exists" % path_src)
+ else:
+ shutil.copytree(path_src, path_dst)
+ # 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()
+ else:
+ self.report({'INFO'}, "Reload Start-Up file to restore settings.")
+ return {'FINISHED'}
+
+ return {'CANCELLED'}
+
+
+def _webbrowser_bug_fix():
+ # test for X11
+ import os
+
+ if os.environ.get("DISPLAY"):
+
+ # BSD licenced code copied from python, temp fix for bug
+ # http://bugs.python.org/issue11432, XXX == added code
+ def _invoke(self, args, remote, autoraise):
+ # XXX, added imports
+ import io
+ import subprocess
+ import time
+ raise_opt = []
+ if remote and self.raise_opts:
+ # use autoraise argument only for remote invocation
+ autoraise = int(autoraise)
+ opt = self.raise_opts[autoraise]
+ if opt:
+ raise_opt = [opt]
-def unregister():
- bpy.utils.unregister_module(__name__)
+ cmdline = [self.name] + raise_opt + args
-if __name__ == "__main__":
- register()
+ if remote or self.background:
+ inout = io.open(os.devnull, "r+")
+ else:
+ # for TTY browsers, we need stdin/out
+ inout = None
+ # if possible, put browser in separate process group, so
+ # keyboard interrupts don't affect browser as well as Python
+ setsid = getattr(os, 'setsid', None)
+ if not setsid:
+ setsid = getattr(os, 'setpgrp', None)
+
+ p = subprocess.Popen(cmdline, close_fds=True, # XXX, stdin=inout,
+ stdout=(self.redirect_stdout and inout or None),
+ stderr=inout, preexec_fn=setsid)
+ if remote:
+ # wait five secons. If the subprocess is not finished, the
+ # remote invocation has (hopefully) started a new instance.
+ time.sleep(1)
+ rc = p.poll()
+ if rc is None:
+ time.sleep(4)
+ rc = p.poll()
+ if rc is None:
+ return True
+ # if remote call failed, open() will try direct invocation
+ return not rc
+ elif self.background:
+ if p.poll() is None:
+ return True
+ else:
+ return False
+ else:
+ return not p.wait()
+
+ import webbrowser
+ webbrowser.UnixBrowser._invoke = _invoke
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
new file mode 100644
index 00000000000..5c565fbd300
--- /dev/null
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -0,0 +1,121 @@
+# ##### 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>
+
+# note, properties_animviz is a helper module only.
+
+if "bpy" in locals():
+ from imp import reload as _reload
+ for val in _modules_loaded.values():
+ _reload(val)
+_modules = (
+ "properties_animviz",
+ "properties_data_armature",
+ "properties_data_bone",
+ "properties_data_camera",
+ "properties_data_curve",
+ "properties_data_empty",
+ "properties_data_lamp",
+ "properties_data_lattice",
+ "properties_data_mesh",
+ "properties_data_metaball",
+ "properties_data_modifier",
+ "properties_game",
+ "properties_material",
+ "properties_object_constraint",
+ "properties_object",
+ "properties_particle",
+ "properties_physics_cloth",
+ "properties_physics_common",
+ "properties_physics_field",
+ "properties_physics_fluid",
+ "properties_physics_smoke",
+ "properties_physics_softbody",
+ "properties_render",
+ "properties_scene",
+ "properties_texture",
+ "properties_world",
+ "space_console",
+ "space_dopesheet",
+ "space_filebrowser",
+ "space_graph",
+ "space_image",
+ "space_info",
+ "space_logic",
+ "space_nla",
+ "space_node",
+ "space_outliner",
+ "space_sequencer",
+ "space_text",
+ "space_time",
+ "space_userpref_keymap",
+ "space_userpref",
+ "space_view3d",
+ "space_view3d_toolbar",
+)
+__import__(name=__name__, fromlist=_modules)
+_namespace = globals()
+_modules_loaded = {name: _namespace[name] for name in _modules}
+del _namespace
+
+
+import bpy
+
+
+def register():
+ bpy.utils.register_module(__name__)
+
+ # space_userprefs.py
+ from bpy.props import StringProperty, EnumProperty
+ WindowManager = bpy.types.WindowManager
+
+ 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", "")
+ ],
+ name="Category",
+ description="Filter add-ons by category",
+ )
+
+ WindowManager.addon_support = EnumProperty(
+ items=[('OFFICIAL', "Official", ""),
+ ('COMMUNITY', 'Community', ""),
+ ],
+ name="Support",
+ description="Display support level", default={'OFFICIAL', 'COMMUNITY'}, options={'ENUM_FLAG'})
+ # done...
+
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
diff --git a/release/scripts/ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py
index 5f5684f5c89..eb1bbfd2fb1 100644
--- a/release/scripts/ui/properties_animviz.py
+++ b/release/scripts/startup/bl_ui/properties_animviz.py
@@ -17,11 +17,13 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
-import bpy
-
# Generic Panels (Independent of DataType)
+# NOTE:
+# The specialised panel types are derived in their respective UI modules
+# dont register these classes since they are only helpers.
+
class MotionPathButtonsPanel():
bl_space_type = 'PROPERTIES'
@@ -91,16 +93,5 @@ class OnionSkinButtonsPanel():
col.label(text="Display:")
col.prop(arm, "show_only_ghost_selected", text="Selected Only")
-
-# NOTE:
-# The specialised panel types are derived in their respective UI modules
-# dont register these classes since they are only helpers.
-def register():
- pass # bpy.utils.register_module(__name__)
-
-
-def unregister():
- pass # bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
+if __name__ == "__main__": # only for live edit.
+ bpy.utils.register_module(__name__)
diff --git a/release/scripts/ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py
index 6550e1bb1ed..9477dc866ab 100644
--- a/release/scripts/ui/properties_data_armature.py
+++ b/release/scripts/startup/bl_ui/properties_data_armature.py
@@ -58,24 +58,17 @@ class DATA_PT_skeleton(ArmatureButtonsPanel, bpy.types.Panel):
layout.prop(arm, "pose_position", expand=True)
- split = layout.split()
-
- col = split.column()
+ col = layout.column()
col.label(text="Layers:")
col.prop(arm, "layers", text="")
col.label(text="Protected Layers:")
col.prop(arm, "layers_protected", text="")
- col.label(text="Deform:")
-
- split = layout.split()
-
- col = split.column()
- col.prop(arm, "use_deform_vertex_groups", text="Vertex Groups")
- col.prop(arm, "use_deform_envelopes", text="Envelopes")
-
- col = split.column()
- col.prop(arm, "use_deform_preserve_volume", text="Quaternion")
+ layout.label(text="Deform:")
+ flow = layout.column_flow()
+ flow.prop(arm, "use_deform_vertex_groups", text="Vertex Groups")
+ flow.prop(arm, "use_deform_envelopes", text="Envelopes")
+ flow.prop(arm, "use_deform_preserve_volume", text="Quaternion")
class DATA_PT_display(ArmatureButtonsPanel, bpy.types.Panel):
@@ -87,7 +80,7 @@ class DATA_PT_display(ArmatureButtonsPanel, bpy.types.Panel):
ob = context.object
arm = context.armature
- layout.row().prop(arm, "draw_type", expand=True)
+ layout.prop(arm, "draw_type", expand=True)
split = layout.split()
@@ -137,10 +130,10 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, bpy.types.Panel):
col.prop(group, "color_set")
if group.color_set:
col = split.column()
- subrow = col.row(align=True)
- subrow.prop(group.colors, "normal", text="")
- subrow.prop(group.colors, "select", text="")
- subrow.prop(group.colors, "active", text="")
+ sub = col.row(align=True)
+ sub.prop(group.colors, "normal", text="")
+ sub.prop(group.colors, "select", text="")
+ sub.prop(group.colors, "active", text="")
row = layout.row()
row.active = (ob.proxy is None)
@@ -154,6 +147,44 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, bpy.types.Panel):
sub.operator("pose.group_deselect", text="Deselect")
+class DATA_PT_pose_library(ArmatureButtonsPanel, bpy.types.Panel):
+ bl_label = "Pose Library"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.object and context.object.type == 'ARMATURE' and context.object.pose)
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ poselib = ob.pose_library
+
+ layout.template_ID(ob, "pose_library", new="poselib.new", unlink="poselib.unlink")
+
+ if poselib:
+ row = layout.row()
+ row.template_list(poselib, "pose_markers", poselib.pose_markers, "active_index", rows=5)
+
+ col = row.column(align=True)
+ col.active = (poselib.library is None)
+
+ # invoke should still be used for 'add', as it is needed to allow
+ # add/replace options to be used properly
+ col.operator("poselib.pose_add", icon='ZOOMIN', text="")
+
+ col.operator_context = 'EXEC_DEFAULT' # exec not invoke, so that menu doesn't need showing
+
+ pose_marker_active = poselib.pose_markers.active
+
+ if pose_marker_active is not None:
+ col.operator("poselib.pose_remove", icon='ZOOMOUT', text="").pose = pose_marker_active.name
+ col.operator("poselib.apply_pose", icon='ZOOM_SELECTED', text="").pose_index = poselib.pose_markers.active_index
+
+ layout.operator("poselib.action_sanitise")
+
+
# TODO: this panel will soon be depreceated too
class DATA_PT_ghost(ArmatureButtonsPanel, bpy.types.Panel):
bl_label = "Ghost"
@@ -167,16 +198,15 @@ class DATA_PT_ghost(ArmatureButtonsPanel, bpy.types.Panel):
split = layout.split()
- col = split.column()
+ col = split.column(align=True)
- sub = col.column(align=True)
if arm.ghost_type == 'RANGE':
- sub.prop(arm, "ghost_frame_start", text="Start")
- sub.prop(arm, "ghost_frame_end", text="End")
- sub.prop(arm, "ghost_size", text="Step")
+ col.prop(arm, "ghost_frame_start", text="Start")
+ col.prop(arm, "ghost_frame_end", text="End")
+ col.prop(arm, "ghost_size", text="Step")
elif arm.ghost_type == 'CURRENT_FRAME':
- sub.prop(arm, "ghost_step", text="Range")
- sub.prop(arm, "ghost_size", text="Step")
+ col.prop(arm, "ghost_step", text="Range")
+ col.prop(arm, "ghost_size", text="Step")
col = split.column()
col.label(text="Display:")
@@ -196,11 +226,9 @@ class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, bpy.types.Panel):
layout = self.layout
ob = context.object
-
itasc = ob.pose.ik_param
- row = layout.row()
- row.prop(ob.pose, "ik_solver")
+ layout.prop(ob.pose, "ik_solver")
if itasc:
layout.prop(itasc, "mode", expand=True)
@@ -209,13 +237,10 @@ class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, bpy.types.Panel):
layout.label(text="Reiteration:")
layout.prop(itasc, "reiteration_method", expand=True)
- split = layout.split()
- split.active = not simulation or itasc.reiteration_method != 'NEVER'
- col = split.column()
- col.prop(itasc, "precision")
-
- col = split.column()
- col.prop(itasc, "iterations")
+ row = layout.row()
+ row.active = not simulation or itasc.reiteration_method != 'NEVER'
+ row.prop(itasc, "precision")
+ row.prop(itasc, "iterations")
if simulation:
layout.prop(itasc, "use_auto_step")
@@ -235,7 +260,10 @@ class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, bpy.types.Panel):
row.prop(itasc, "damping_max", text="Damp", slider=True)
row.prop(itasc, "damping_epsilon", text="Eps", slider=True)
-from properties_animviz import MotionPathButtonsPanel, OnionSkinButtonsPanel
+from bl_ui.properties_animviz import (
+ MotionPathButtonsPanel,
+ OnionSkinButtonsPanel,
+ )
class DATA_PT_motion_paths(MotionPathButtonsPanel, bpy.types.Panel):
@@ -257,12 +285,8 @@ class DATA_PT_motion_paths(MotionPathButtonsPanel, bpy.types.Panel):
layout.separator()
split = layout.split()
-
- col = split.column()
- col.operator("pose.paths_calculate", text="Calculate Paths")
-
- col = split.column()
- col.operator("pose.paths_clear", text="Clear Paths")
+ split.operator("pose.paths_calculate", text="Calculate Paths")
+ split.operator("pose.paths_clear", text="Clear Paths")
class DATA_PT_onion_skinning(OnionSkinButtonsPanel): # , bpy.types.Panel): # inherit from panel when ready
@@ -287,13 +311,5 @@ class DATA_PT_custom_props_arm(ArmatureButtonsPanel, PropertyPanel, bpy.types.Pa
_context_path = "object.data"
_property_type = bpy.types.Armature
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py
index feca4fc2502..9fc055e9343 100644
--- a/release/scripts/ui/properties_data_bone.py
+++ b/release/scripts/startup/bl_ui/properties_data_bone.py
@@ -17,6 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
+
import bpy
from rna_prop_ui import PropertyPanel
@@ -123,7 +124,7 @@ class BONE_PT_transform_locks(BoneButtonsPanel, bpy.types.Panel):
col.active = not (bone.parent and bone.use_connect)
col = row.column()
- if pchan.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'):
+ if pchan.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
col.prop(pchan, "lock_rotations_4d", text="Lock Rotation")
if pchan.lock_rotations_4d:
col.prop(pchan, "lock_rotation_w", text="W")
@@ -368,13 +369,5 @@ class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, bpy.types.Panel):
else:
return "active_bone"
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index 0f5d4a59bf5..e5076fd20d5 100644
--- a/release/scripts/ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -78,7 +78,14 @@ class DATA_PT_camera(CameraButtonsPanel, bpy.types.Panel):
elif cam.type == 'ORTHO':
col.prop(cam, "ortho_scale")
- layout.prop(cam, "use_panorama")
+ col = layout.column()
+ if cam.type == 'ORTHO':
+ if cam.use_panorama:
+ col.alert = True
+ else:
+ col.enabled = False
+
+ col.prop(cam, "use_panorama")
split = layout.split()
@@ -95,9 +102,7 @@ class DATA_PT_camera(CameraButtonsPanel, bpy.types.Panel):
layout.label(text="Depth of Field:")
split = layout.split()
-
- col = split.column()
- col.prop(cam, "dof_object", text="")
+ split.prop(cam, "dof_object", text="")
col = split.column()
@@ -137,13 +142,5 @@ class DATA_PT_custom_props_camera(CameraButtonsPanel, PropertyPanel, bpy.types.P
_context_path = "object.data"
_property_type = bpy.types.Camera
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py
index b26b7140fd2..623daffac93 100644
--- a/release/scripts/ui/properties_data_curve.py
+++ b/release/scripts/startup/bl_ui/properties_data_curve.py
@@ -28,7 +28,7 @@ class CurveButtonsPanel():
@classmethod
def poll(cls, context):
- return (context.object and context.object.type in ('CURVE', 'SURFACE', 'FONT') and context.curve)
+ return (context.object and context.object.type in {'CURVE', 'SURFACE', 'FONT'} and context.curve)
class CurveButtonsPanelCurve(CurveButtonsPanel):
@@ -106,12 +106,12 @@ class DATA_PT_shape_curve(CurveButtonsPanel, bpy.types.Panel):
sub.prop(curve, "render_resolution_v", text="Render V")
if (is_curve or is_text):
+ col.label(text="Fill:")
sub = col.column()
sub.active = (curve.bevel_object is None)
- sub.label(text="Fill:")
sub.prop(curve, "use_fill_front")
sub.prop(curve, "use_fill_back")
- sub.prop(curve, "use_fill_deform", text="Fill Deformed")
+ col.prop(curve, "use_fill_deform", text="Fill Deformed")
col.label(text="Textures:")
col.prop(curve, "use_uv_as_generated")
@@ -312,12 +312,9 @@ class DATA_PT_font(CurveButtonsPanel, bpy.types.Panel):
col.prop(char, "use_italic")
col.prop(char, "use_underline")
- split = layout.split()
- col = split.column()
- col.prop(text, "small_caps_scale", text="Small Caps")
-
- col = split.column()
- col.prop(char, "use_small_caps")
+ row = layout.row()
+ row.prop(text, "small_caps_scale", text="Small Caps")
+ row.prop(char, "use_small_caps")
class DATA_PT_paragraph(CurveButtonsPanel, bpy.types.Panel):
@@ -394,13 +391,5 @@ class DATA_PT_custom_props_curve(CurveButtonsPanel, PropertyPanel, bpy.types.Pan
_context_path = "object.data"
_property_type = bpy.types.Curve
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_empty.py b/release/scripts/startup/bl_ui/properties_data_empty.py
index bb0028efec5..e46cd1270ad 100644
--- a/release/scripts/ui/properties_data_empty.py
+++ b/release/scripts/startup/bl_ui/properties_data_empty.py
@@ -39,16 +39,7 @@ class DATA_PT_empty(DataButtonsPanel, bpy.types.Panel):
ob = context.object
layout.prop(ob, "empty_draw_type", text="Display")
-
layout.prop(ob, "empty_draw_size", text="Size")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_lamp.py b/release/scripts/startup/bl_ui/properties_data_lamp.py
index 55781434763..36010c8b511 100644
--- a/release/scripts/ui/properties_data_lamp.py
+++ b/release/scripts/startup/bl_ui/properties_data_lamp.py
@@ -91,7 +91,7 @@ class DATA_PT_lamp(DataButtonsPanel, bpy.types.Panel):
sub.prop(lamp, "color", text="")
sub.prop(lamp, "energy")
- if lamp.type in ('POINT', 'SPOT'):
+ if lamp.type in {'POINT', 'SPOT'}:
sub.label(text="Falloff:")
sub.prop(lamp, "falloff_type", text="")
sub.prop(lamp, "distance")
@@ -195,7 +195,7 @@ class DATA_PT_shadow(DataButtonsPanel, bpy.types.Panel):
def poll(cls, context):
lamp = context.lamp
engine = context.scene.render.engine
- return (lamp and lamp.type in ('POINT', 'SUN', 'SPOT', 'AREA')) and (engine in cls.COMPAT_ENGINES)
+ return (lamp and lamp.type in {'POINT', 'SUN', 'SPOT', 'AREA'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -234,7 +234,7 @@ class DATA_PT_shadow(DataButtonsPanel, bpy.types.Panel):
col = split.column()
col.label(text="Sampling:")
- if lamp.type in ('POINT', 'SUN', 'SPOT'):
+ if lamp.type in {'POINT', 'SUN', 'SPOT'}:
sub = col.row()
sub.prop(lamp, "shadow_ray_samples", text="Samples")
@@ -251,26 +251,21 @@ class DATA_PT_shadow(DataButtonsPanel, bpy.types.Panel):
col.row().prop(lamp, "shadow_ray_sample_method", expand=True)
- split = layout.split()
- col = split.column()
-
if lamp.shadow_ray_sample_method == 'ADAPTIVE_QMC':
- col.prop(lamp, "shadow_adaptive_threshold", text="Threshold")
- col = split.column()
+ layout.prop(lamp, "shadow_adaptive_threshold", text="Threshold")
if lamp.type == 'AREA' and lamp.shadow_ray_sample_method == 'CONSTANT_JITTERED':
- col = split.column()
- col = split.column()
- col.prop(lamp, "use_umbra")
- col.prop(lamp, "use_dither")
- col.prop(lamp, "use_jitter")
+ row = layout.row()
+ row.prop(lamp, "use_umbra")
+ row.prop(lamp, "use_dither")
+ row.prop(lamp, "use_jitter")
elif lamp.shadow_method == 'BUFFER_SHADOW':
col = layout.column()
col.label(text="Buffer Type:")
col.row().prop(lamp, "shadow_buffer_type", expand=True)
- if lamp.shadow_buffer_type in ('REGULAR', 'HALFWAY', 'DEEP'):
+ if lamp.shadow_buffer_type in {'REGULAR', 'HALFWAY', 'DEEP'}:
split = layout.split()
col = split.column()
@@ -318,13 +313,11 @@ class DATA_PT_area(DataButtonsPanel, bpy.types.Panel):
return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
- lamp = context.lamp
-
layout = self.layout
- split = layout.split()
- col = split.column()
+ lamp = context.lamp
+ col = layout.column()
col.row().prop(lamp, "shape", expand=True)
sub = col.row(align=True)
@@ -379,7 +372,7 @@ class DATA_PT_falloff_curve(DataButtonsPanel, bpy.types.Panel):
lamp = context.lamp
engine = context.scene.render.engine
- return (lamp and lamp.type in ('POINT', 'SPOT') and lamp.falloff_type == 'CUSTOM_CURVE') and (engine in cls.COMPAT_ENGINES)
+ return (lamp and lamp.type in {'POINT', 'SPOT'} and lamp.falloff_type == 'CUSTOM_CURVE') and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
lamp = context.lamp
@@ -392,13 +385,5 @@ class DATA_PT_custom_props_lamp(DataButtonsPanel, PropertyPanel, bpy.types.Panel
_context_path = "object.data"
_property_type = bpy.types.Lamp
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_lattice.py b/release/scripts/startup/bl_ui/properties_data_lattice.py
index 6bdb36d3520..cd719b6fe84 100644
--- a/release/scripts/ui/properties_data_lattice.py
+++ b/release/scripts/startup/bl_ui/properties_data_lattice.py
@@ -59,23 +59,17 @@ class DATA_PT_lattice(DataButtonsPanel, bpy.types.Panel):
lat = context.lattice
- split = layout.split()
- col = split.column()
- col.prop(lat, "points_u")
- col = split.column()
- col.prop(lat, "interpolation_type_u", text="")
-
- split = layout.split()
- col = split.column()
- col.prop(lat, "points_v")
- col = split.column()
- col.prop(lat, "interpolation_type_v", text="")
-
- split = layout.split()
- col = split.column()
- col.prop(lat, "points_w")
- col = split.column()
- col.prop(lat, "interpolation_type_w", text="")
+ row = layout.row()
+ row.prop(lat, "points_u")
+ row.prop(lat, "interpolation_type_u", text="")
+
+ row = layout.row()
+ row.prop(lat, "points_v")
+ row.prop(lat, "interpolation_type_v", text="")
+
+ row = layout.row()
+ row.prop(lat, "points_w")
+ row.prop(lat, "interpolation_type_w", text="")
row = layout.row()
row.prop(lat, "use_outside")
@@ -87,13 +81,5 @@ class DATA_PT_custom_props_lattice(DataButtonsPanel, PropertyPanel, bpy.types.Pa
_context_path = "object.data"
_property_type = bpy.types.Lattice
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 342204d0e78..b1d1789fadd 100644
--- a/release/scripts/ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -94,9 +94,7 @@ class DATA_PT_normals(MeshButtonsPanel, bpy.types.Panel):
sub.active = mesh.use_auto_smooth
sub.prop(mesh, "auto_smooth_angle", text="Angle")
- col = split.column()
-
- col.prop(mesh, "show_double_sided")
+ split.prop(mesh, "show_double_sided")
class DATA_PT_settings(MeshButtonsPanel, bpy.types.Panel):
@@ -120,7 +118,7 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, bpy.types.Panel):
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return (obj and obj.type in ('MESH', 'LATTICE') and (engine in cls.COMPAT_ENGINES))
+ return (obj and obj.type in {'MESH', 'LATTICE'} and (engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
@@ -169,7 +167,7 @@ class DATA_PT_shape_keys(MeshButtonsPanel, bpy.types.Panel):
def poll(cls, context):
engine = context.scene.render.engine
obj = context.object
- return (obj and obj.type in ('MESH', 'LATTICE', 'CURVE', 'SURFACE') and (engine in cls.COMPAT_ENGINES))
+ return (obj and obj.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'} and (engine in cls.COMPAT_ENGINES))
def draw(self, context):
layout = self.layout
@@ -190,7 +188,7 @@ class DATA_PT_shape_keys(MeshButtonsPanel, bpy.types.Panel):
rows = 2
if kb:
rows = 5
- row.template_list(key, "keys", ob, "active_shape_key_index", rows=rows)
+ row.template_list(key, "key_blocks", ob, "active_shape_key_index", rows=rows)
col = row.column()
@@ -245,7 +243,7 @@ class DATA_PT_shape_keys(MeshButtonsPanel, bpy.types.Panel):
col.active = enable_edit_value
col.label(text="Blend:")
col.prop_search(kb, "vertex_group", ob, "vertex_groups", text="")
- col.prop_search(kb, "relative_key", key, "keys", text="")
+ col.prop_search(kb, "relative_key", key, "key_blocks", text="")
else:
row = layout.row()
@@ -283,7 +281,6 @@ class DATA_PT_texface(MeshButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
ob = context.active_object
- rd = context.scene.render
return (context.mode == 'EDIT_MESH') and ob and ob.type == 'MESH'
@@ -353,13 +350,5 @@ class DATA_PT_custom_props_mesh(MeshButtonsPanel, PropertyPanel, bpy.types.Panel
_context_path = "object.data"
_property_type = bpy.types.Mesh
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_metaball.py b/release/scripts/startup/bl_ui/properties_data_metaball.py
index cf7aa8c08fd..81ba15d6f40 100644
--- a/release/scripts/ui/properties_data_metaball.py
+++ b/release/scripts/startup/bl_ui/properties_data_metaball.py
@@ -96,7 +96,7 @@ class DATA_PT_metaball_element(DataButtonsPanel, bpy.types.Panel):
col = split.column(align=True)
- if metaelem.type in ('CUBE', 'ELLIPSOID'):
+ if metaelem.type in {'CUBE', 'ELLIPSOID'}:
col.label(text="Size:")
col.prop(metaelem, "size_x", text="X")
col.prop(metaelem, "size_y", text="Y")
@@ -117,13 +117,5 @@ class DATA_PT_custom_props_metaball(DataButtonsPanel, PropertyPanel, bpy.types.P
_context_path = "object.data"
_property_type = bpy.types.MetaBall
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index f17cd8a4b98..95db7d216fb 100644
--- a/release/scripts/ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -59,16 +59,15 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "use_vertex_groups", text="Vertex Groups")
col.prop(md, "use_bone_envelopes", text="Bone Envelopes")
- split = layout.split()
+ layout.separator()
- col = split.split()
- col.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
- sub = col.column()
+ row = layout.row()
+ row.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
+ sub = row.row()
sub.active = bool(md.vertex_group)
sub.prop(md, "invert_vertex_group")
- col = layout.column()
- col.prop(md, "use_multi_modifier")
+ layout.prop(md, "use_multi_modifier")
def ARRAY(self, layout, ob, md):
layout.prop(md, "fit_type")
@@ -113,18 +112,14 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
layout.separator()
- col = layout.column()
- col.prop(md, "start_cap")
- col.prop(md, "end_cap")
+ layout.prop(md, "start_cap")
+ layout.prop(md, "end_cap")
def BEVEL(self, layout, ob, md):
split = layout.split()
- col = split.column()
- col.prop(md, "width")
-
- col = split.column()
- col.prop(md, "use_only_vertices")
+ split.prop(md, "width")
+ split.prop(md, "use_only_vertices")
layout.label(text="Limit Method:")
layout.row().prop(md, "limit_method", expand=True)
@@ -188,10 +183,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "use_transform")
def CLOTH(self, layout, ob, md):
- layout.label(text="See Cloth panel.")
+ layout.label(text="Settings can be found inside the Physics context")
def COLLISION(self, layout, ob, md):
- layout.label(text="See Collision panel.")
+ layout.label(text="Settings can be found inside the Physics context")
def CURVE(self, layout, ob, md):
split = layout.split()
@@ -214,7 +209,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col = split.column()
col.label(text="Texture:")
- col.prop(md, "texture", text="")
+ col.template_ID(md, "texture", new="texture.new")
col.label(text="Vertex Group:")
col.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
@@ -230,13 +225,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
layout.separator()
- split = layout.split()
-
- col = split.column()
- col.prop(md, "mid_level")
-
- col = split.column()
- col.prop(md, "strength")
+ row = layout.row()
+ row.prop(md, "mid_level")
+ row.prop(md, "strength")
def EDGE_SPLIT(self, layout, ob, md):
split = layout.split()
@@ -247,8 +238,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
sub.active = md.use_edge_angle
sub.prop(md, "split_angle")
- col = split.column()
- col.prop(md, "use_edge_sharp", text="Sharp Edges")
+ split.prop(md, "use_edge_sharp", text="Sharp Edges")
def EXPLODE(self, layout, ob, md):
split = layout.split()
@@ -259,9 +249,11 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
sub = col.column()
sub.active = bool(md.vertex_group)
sub.prop(md, "protect")
+ col.label(text="Particle UV")
+ col.prop_search(md, "particle_uv", ob.data, "uv_textures", text="")
col = split.column()
- col.prop(md, "use_edge_split")
+ col.prop(md, "use_edge_cut")
col.prop(md, "show_unborn")
col.prop(md, "show_alive")
col.prop(md, "show_dead")
@@ -270,7 +262,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
layout.operator("object.explode_refresh", text="Refresh")
def FLUID_SIMULATION(self, layout, ob, md):
- layout.label(text="See Fluid panel.")
+ layout.label(text="Settings can be found inside the Physics context")
def HOOK(self, layout, ob, md):
split = layout.split()
@@ -334,6 +326,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
def MESH_DEFORM(self, layout, ob, md):
split = layout.split()
+
col = split.column()
sub = col.column()
sub.label(text="Object:")
@@ -354,13 +347,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
else:
layout.operator("object.meshdeform_bind", text="Bind")
- split = layout.split()
-
- col = split.column()
- col.prop(md, "precision")
-
- col = split.column()
- col.prop(md, "use_dynamic_bind")
+ row = layout.row()
+ row.prop(md, "precision")
+ row.prop(md, "use_dynamic_bind")
def MIRROR(self, layout, ob, md):
split = layout.split(percentage=0.25)
@@ -383,7 +372,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "use_mirror_v", text="V")
col = layout.column()
-
+
if md.use_mirror_merge == True:
col.prop(md, "merge_threshold")
col.label(text="Mirror Object:")
@@ -393,9 +382,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
split = layout.split()
if ob.mode == 'EDIT':
col = split.column()
- col.operator("object.assign_navpolygon", text="Assign poly idx")
- col = split.column()
- col.operator("object.assign_new_navpolygon", text="Assign new poly idx")
+ col.operator("object.assign_navpolygon", text="Assign poly idx")
+ col = split.column()
+ col.operator("object.assign_new_navpolygon", text="Assign new poly idx")
def MULTIRES(self, layout, ob, md):
layout.row().prop(md, "subdivision_type", expand=True)
@@ -460,7 +449,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "random_position", text="Random", slider=True)
def PARTICLE_SYSTEM(self, layout, ob, md):
- layout.label(text="See Particle panel.")
+ layout.label(text="Settings can be found inside the Particle context")
def SCREW(self, layout, ob, md):
split = layout.split()
@@ -520,8 +509,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col = split.column()
col.label(text="Cull Faces:")
- col.prop(md, "use_cull_front_faces", text="Front")
- col.prop(md, "use_cull_back_faces", text="Back")
+ col.prop(md, "cull_face", expand=True)
layout.label(text="Auxiliary Target:")
layout.prop(md, "auxiliary_target", text="")
@@ -553,12 +541,12 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.label(text="Deform:")
col.prop(md, "factor")
col.prop(md, "limits", slider=True)
- if md.deform_method in ('TAPER', 'STRETCH'):
+ if md.deform_method in {'TAPER', 'STRETCH'}:
col.prop(md, "lock_x")
col.prop(md, "lock_y")
def SMOKE(self, layout, ob, md):
- layout.label(text="See Smoke panel.")
+ layout.label(text="Settings can be found inside the Physics context")
def SMOOTH(self, layout, ob, md):
split = layout.split(percentage=0.25)
@@ -576,10 +564,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
def SOFT_BODY(self, layout, ob, md):
- layout.label(text="See Soft Body panel.")
+ layout.label(text="Settings can be found inside the Physics context")
def SOLIDIFY(self, layout, ob, md):
-
split = layout.split()
col = split.column()
@@ -590,21 +577,26 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "edge_crease_inner", text="Inner")
col.prop(md, "edge_crease_outer", text="Outer")
col.prop(md, "edge_crease_rim", text="Rim")
+ col.label(text="Material Index Offset:")
col = split.column()
col.prop(md, "offset")
- colsub = col.column()
- colsub.active = bool(md.vertex_group)
- colsub.prop(md, "invert_vertex_group", text="Invert")
+ sub = col.column()
+ sub.active = bool(md.vertex_group)
+ sub.prop(md, "invert_vertex_group", text="Invert")
col.prop(md, "use_even_offset")
col.prop(md, "use_quality_normals")
-
col.prop(md, "use_rim")
- colsub = col.column()
- colsub.active = md.use_rim
- colsub.prop(md, "use_rim_material")
+
+ sub = col.column()
+ sub.label()
+ row = sub.split(align=True, percentage=0.4)
+ row.prop(md, "material_offset", text="")
+ row = row.row()
+ row.active = md.use_rim
+ row.prop(md, "material_offset_rim", text="Rim")
def SUBSURF(self, layout, ob, md):
layout.row().prop(md, "subdivision_type", expand=True)
@@ -621,7 +613,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "show_only_control_edges")
def SURFACE(self, layout, ob, md):
- layout.label(text="See Fields panel.")
+ layout.label(text="Settings can be found inside the Physics context")
def UV_PROJECT(self, layout, ob, md):
if ob.type == 'MESH':
@@ -651,6 +643,48 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
sub.prop(md, "scale_x", text="Scale X")
sub.prop(md, "scale_y", text="Scale Y")
+ def WARP(self, layout, ob, md):
+ use_falloff = (md.falloff_type != 'NONE')
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="From:")
+ col.prop(md, "object_from", text="")
+
+ col.prop(md, "use_volume_preserve")
+
+ col = split.column()
+ col.label(text="To:")
+ col.prop(md, "object_to", text="")
+ col.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
+
+ col = layout.column()
+
+ row = col.row(align=True)
+ row.prop(md, "strength")
+ if use_falloff:
+ row.prop(md, "falloff_radius")
+
+ col.prop(md, "falloff_type")
+ if use_falloff:
+ if md.falloff_type == 'CURVE':
+ col.template_curve_mapping(md, "falloff_curve")
+
+ # 2 new columns
+ split = layout.split()
+ col = split.column()
+ col.label(text="Texture:")
+ col.prop(md, "texture", text="")
+
+ col = split.column()
+ col.label(text="Texture Coordinates:")
+ col.prop(md, "texture_coords", text="")
+
+ if md.texture_coords == 'OBJECT':
+ layout.prop(md, "texture_coordinate_object", text="Object")
+ elif md.texture_coords == 'UV' and ob.type == 'MESH':
+ layout.prop_search(md, "uv_layer", ob.data, "uv_textures")
+
def WAVE(self, layout, ob, md):
split = layout.split()
@@ -688,7 +722,11 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
layout.prop(md, "start_position_object")
layout.prop_search(md, "vertex_group", ob, "vertex_groups")
- layout.prop(md, "texture")
+ split = layout.split(percentage=0.33)
+ col = split.column()
+ col.label(text="Texture")
+ col = split.column()
+ col.template_ID(md, "texture", new="texture.new")
layout.prop(md, "texture_coords")
if md.texture_coords == 'MAP_UV' and ob.type == 'MESH':
layout.prop_search(md, "uv_layer", ob.data, "uv_textures")
@@ -707,13 +745,5 @@ class DATA_PT_modifiers(ModifierButtonsPanel, bpy.types.Panel):
col.prop(md, "width", slider=True)
col.prop(md, "narrowness", slider=True)
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py
index 60934891b7f..6952f3c31db 100644
--- a/release/scripts/ui/properties_game.py
+++ b/release/scripts/startup/bl_ui/properties_game.py
@@ -47,7 +47,7 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, bpy.types.Panel):
layout.separator()
#if game.physics_type == 'DYNAMIC':
- if game.physics_type in ('DYNAMIC', 'RIGID_BODY'):
+ if game.physics_type in {'DYNAMIC', 'RIGID_BODY'}:
split = layout.split()
col = split.column()
@@ -56,7 +56,7 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, bpy.types.Panel):
col.prop(ob, "hide_render", text="Invisible") # out of place but useful
col = split.column()
- col.prop(game, "use_material_physics")
+ col.prop(game, "use_material_physics_fh")
col.prop(game, "use_rotate_from_normal")
col.prop(game, "use_sleep")
@@ -163,7 +163,7 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, bpy.types.Panel):
subsub.active = game.use_anisotropic_friction
subsub.prop(game, "friction_coefficients", text="", slider=True)
- elif game.physics_type in ('SENSOR', 'INVISIBLE', 'NO_COLLISION', 'OCCLUDE'):
+ elif game.physics_type in {'SENSOR', 'INVISIBLE', 'NO_COLLISION', 'OCCLUDE'}:
layout.prop(ob, "hide_render", text="Invisible")
@@ -175,7 +175,7 @@ class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel, bpy.types.Panel):
def poll(cls, context):
game = context.object.game
rd = context.scene.render
- return (game.physics_type in ('DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC')) and (rd.engine in cls.COMPAT_ENGINES)
+ return (game.physics_type in {'DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC'}) and (rd.engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
game = context.active_object.game
@@ -190,18 +190,14 @@ class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel, bpy.types.Panel):
layout.active = game.use_collision_bounds
layout.prop(game, "collision_bounds_type", text="Bounds")
- split = layout.split()
-
- col = split.column()
- col.prop(game, "collision_margin", text="Margin", slider=True)
-
- col = split.column()
- col.prop(game, "use_collision_compound", text="Compound")
+ row = layout.row()
+ row.prop(game, "collision_margin", text="Margin", slider=True)
+ row.prop(game, "use_collision_compound", text="Compound")
class PHYSICS_PT_game_obstacles(PhysicsButtonsPanel, bpy.types.Panel):
bl_label = "Create obstacle"
COMPAT_ENGINES = {'BLENDER_GAME'}
-
+
@classmethod
def poll(self, context):
game = context.object.game
@@ -222,7 +218,7 @@ class PHYSICS_PT_game_obstacles(PhysicsButtonsPanel, bpy.types.Panel):
split = layout.split()
col = split.column()
- col.prop(game, "obstacle_radius", text="Radius")
+ col.prop(game, "obstacle_radius", text="Radius")
class RenderButtonsPanel():
bl_space_type = 'PROPERTIES'
@@ -352,6 +348,7 @@ class RENDER_PT_game_shading(RenderButtonsPanel, bpy.types.Panel):
col.prop(gs, "use_glsl_lights", text="Lights")
col.prop(gs, "use_glsl_shaders", text="Shaders")
col.prop(gs, "use_glsl_shadows", text="Shadows")
+ col.prop(gs, "use_glsl_color_management", text="Color Management")
col = split.column()
col.prop(gs, "use_glsl_ramps", text="Ramps")
@@ -385,7 +382,7 @@ class RENDER_PT_game_display(RenderButtonsPanel, bpy.types.Panel):
flow.prop(gs, "show_framerate_profile", text="Framerate and Profile")
flow.prop(gs, "show_physics_visualization", text="Physics Visualization")
flow.prop(gs, "use_deprecation_warnings")
- flow.prop(gs, "show_mouse")
+ flow.prop(gs, "show_mouse", text="Mouse Cursor")
class RENDER_PT_game_sound(RenderButtonsPanel, bpy.types.Panel):
@@ -447,13 +444,9 @@ class WORLD_PT_game_world(WorldButtonsPanel, bpy.types.Panel):
world = context.world
- split = layout.split()
-
- col = split.column()
- col.prop(world, "horizon_color")
-
- col = split.column()
- col.prop(world, "ambient_color")
+ row = layout.row()
+ row.column().prop(world, "horizon_color")
+ row.column().prop(world, "ambient_color")
class WORLD_PT_game_mist(WorldButtonsPanel, bpy.types.Panel):
@@ -476,13 +469,10 @@ class WORLD_PT_game_mist(WorldButtonsPanel, bpy.types.Panel):
world = context.world
layout.active = world.mist_settings.use_mist
- split = layout.split()
-
- col = split.column()
- col.prop(world.mist_settings, "start")
- col = split.column()
- col.prop(world.mist_settings, "depth")
+ row = layout.row()
+ row.prop(world.mist_settings, "start")
+ row.prop(world.mist_settings, "depth")
class WORLD_PT_game_physics(WorldButtonsPanel, bpy.types.Panel):
@@ -547,12 +537,5 @@ class WORLD_PT_game_physics_obstacles(WorldButtonsPanel, bpy.types.Panel):
layout.prop(gs, "level_height")
layout.prop(gs, "show_obstacle_simulation")
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index 82c3e840420..52d6b5f1376 100644
--- a/release/scripts/ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -34,6 +34,22 @@ def active_node_mat(mat):
return None
+def check_material(mat):
+ if mat is not None:
+ if mat.use_nodes:
+ if mat.active_node_material is not None:
+ return True
+ return False
+ return True
+ return False
+
+
+def simple_material(mat):
+ if (mat is not None) and (not mat.use_nodes):
+ return True
+ return False
+
+
class MATERIAL_MT_sss_presets(bpy.types.Menu):
bl_label = "SSS Presets"
preset_subdir = "sss"
@@ -119,6 +135,13 @@ class MATERIAL_PT_context_material(MaterialButtonsPanel, bpy.types.Panel):
if mat:
layout.prop(mat, "type", expand=True)
+ if mat.use_nodes:
+ row = layout.row()
+ row.label(text="", icon='NODETREE')
+ if mat.active_node_material:
+ row.prop(mat.active_node_material, "name", text="")
+ else:
+ row.label(text="No material node selected")
class MATERIAL_PT_preview(MaterialButtonsPanel, bpy.types.Panel):
@@ -129,15 +152,64 @@ class MATERIAL_PT_preview(MaterialButtonsPanel, bpy.types.Panel):
self.layout.template_preview(context.material)
+class MATERIAL_PT_pipeline(MaterialButtonsPanel, bpy.types.Panel):
+ bl_label = "Render Pipeline Options"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ engine = context.scene.render.engine
+ return mat and (not simple_material(mat)) and (mat.type in {'SURFACE', 'WIRE', 'VOLUME'}) and (engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self. layout
+
+ mat = context.material
+ mat_type = mat.type in {'SURFACE', 'WIRE'}
+
+ row = layout.row()
+ row.active = mat_type
+ 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()
+ row.active = mat.use_transparency or not mat_type
+ row.prop(mat, "transparency_method", expand=True)
+
+ layout.separator()
+
+ split = layout.split()
+ col = split.column()
+
+ col.prop(mat, "use_raytrace")
+ col.prop(mat, "use_full_oversampling")
+ sub = col.column()
+ sub.active = mat_type
+ sub.prop(mat, "use_sky")
+ sub.prop(mat, "invert_z")
+
+ col = split.column()
+ col.active = mat_type
+
+ col.prop(mat, "use_cast_shadows_only", text="Cast Only")
+ col.prop(mat, "shadow_cast_alpha", text="Casting Alpha")
+ col.prop(mat, "use_cast_buffer_shadows")
+ col.prop(mat, "use_cast_approximate")
+
+
class MATERIAL_PT_diffuse(MaterialButtonsPanel, bpy.types.Panel):
bl_label = "Diffuse"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -164,36 +236,24 @@ class MATERIAL_PT_diffuse(MaterialButtonsPanel, bpy.types.Panel):
elif mat.diffuse_shader == 'MINNAERT':
col.prop(mat, "darkness")
elif mat.diffuse_shader == 'TOON':
- split = col.split()
-
- col = split.column()
- col.prop(mat, "diffuse_toon_size", text="Size")
-
- col = split.column()
- col.prop(mat, "diffuse_toon_smooth", text="Smooth")
+ row = col.row()
+ row.prop(mat, "diffuse_toon_size", text="Size")
+ row.prop(mat, "diffuse_toon_smooth", text="Smooth")
elif mat.diffuse_shader == 'FRESNEL':
- split = col.split()
-
- col = split.column()
- col.prop(mat, "diffuse_fresnel", text="Fresnel")
-
- col = split.column()
- col.prop(mat, "diffuse_fresnel_factor", text="Factor")
+ row = col.row()
+ row.prop(mat, "diffuse_fresnel", text="Fresnel")
+ row.prop(mat, "diffuse_fresnel_factor", text="Factor")
if mat.use_diffuse_ramp:
layout.separator()
layout.template_color_ramp(mat, "diffuse_ramp", expand=True)
layout.separator()
- split = layout.split()
-
- col = split.column()
- col.prop(mat, "diffuse_ramp_input", text="Input")
-
- col = split.column()
- col.prop(mat, "diffuse_ramp_blend", text="Blend")
row = layout.row()
- row.prop(mat, "diffuse_ramp_factor", text="Factor")
+ row.prop(mat, "diffuse_ramp_input", text="Input")
+ row.prop(mat, "diffuse_ramp_blend", text="Blend")
+
+ layout.prop(mat, "diffuse_ramp_factor", text="Factor")
class MATERIAL_PT_specular(MaterialButtonsPanel, bpy.types.Panel):
@@ -202,9 +262,9 @@ class MATERIAL_PT_specular(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -224,41 +284,29 @@ class MATERIAL_PT_specular(MaterialButtonsPanel, bpy.types.Panel):
col.prop(mat, "use_specular_ramp", text="Ramp")
col = layout.column()
- if mat.specular_shader in ('COOKTORR', 'PHONG'):
+ if mat.specular_shader in {'COOKTORR', 'PHONG'}:
col.prop(mat, "specular_hardness", text="Hardness")
elif mat.specular_shader == 'BLINN':
- split = layout.split()
-
- col = split.column()
- col.prop(mat, "specular_hardness", text="Hardness")
-
- col = split.column()
- col.prop(mat, "specular_ior", text="IOR")
+ row = col.row()
+ row.prop(mat, "specular_hardness", text="Hardness")
+ row.prop(mat, "specular_ior", text="IOR")
elif mat.specular_shader == 'WARDISO':
col.prop(mat, "specular_slope", text="Slope")
elif mat.specular_shader == 'TOON':
- split = layout.split()
-
- col = split.column()
- col.prop(mat, "specular_toon_size", text="Size")
-
- col = split.column()
- col.prop(mat, "specular_toon_smooth", text="Smooth")
+ row = col.row()
+ row.prop(mat, "specular_toon_size", text="Size")
+ row.prop(mat, "specular_toon_smooth", text="Smooth")
if mat.use_specular_ramp:
layout.separator()
layout.template_color_ramp(mat, "specular_ramp", expand=True)
layout.separator()
- split = layout.split()
-
- col = split.column()
- col.prop(mat, "specular_ramp_input", text="Input")
-
- col = split.column()
- col.prop(mat, "specular_ramp_blend", text="Blend")
row = layout.row()
- row.prop(mat, "specular_ramp_factor", text="Factor")
+ row.prop(mat, "specular_ramp_input", text="Input")
+ row.prop(mat, "specular_ramp_blend", text="Blend")
+
+ layout.prop(mat, "specular_ramp_factor", text="Factor")
class MATERIAL_PT_shading(MaterialButtonsPanel, bpy.types.Panel):
@@ -267,16 +315,16 @@ class MATERIAL_PT_shading(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
mat = active_node_mat(context.material)
- if mat.type in ('SURFACE', 'WIRE'):
+ if mat.type in {'SURFACE', 'WIRE'}:
split = layout.split()
col = split.column()
@@ -297,36 +345,40 @@ class MATERIAL_PT_shading(MaterialButtonsPanel, bpy.types.Panel):
class MATERIAL_PT_transp(MaterialButtonsPanel, bpy.types.Panel):
bl_label = "Transparency"
- bl_options = {'DEFAULT_CLOSED'}
+ # bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER'}
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
- mat = active_node_mat(context.material)
+ mat = context.material
- self.layout.prop(mat, "use_transparency", text="")
+ if simple_material(mat):
+ self.layout.prop(mat, "use_transparency", text="")
def draw(self, context):
layout = self.layout
+ base_mat = context.material
mat = active_node_mat(context.material)
rayt = mat.raytrace_transparency
- row = layout.row()
- row.active = mat.use_transparency and (not mat.use_shadeless)
- row.prop(mat, "transparency_method", expand=True)
+ if simple_material(base_mat):
+ row = layout.row()
+ row.active = mat.use_transparency
+ row.prop(mat, "transparency_method", expand=True)
split = layout.split()
+ split.active = base_mat.use_transparency
col = split.column()
col.prop(mat, "alpha")
row = col.row()
- row.active = mat.use_transparency and (not mat.use_shadeless)
+ row.active = (base_mat.transparency_method != 'MASK') and (not mat.use_shadeless)
row.prop(mat, "specular_alpha", text="Specular")
col = split.column()
@@ -336,10 +388,10 @@ class MATERIAL_PT_transp(MaterialButtonsPanel, bpy.types.Panel):
sub.active = rayt.fresnel > 0
sub.prop(rayt, "fresnel_factor", text="Blend")
- if mat.transparency_method == 'RAYTRACE':
+ if base_mat.transparency_method == 'RAYTRACE':
layout.separator()
split = layout.split()
- split.active = mat.use_transparency
+ split.active = base_mat.use_transparency
col = split.column()
col.prop(rayt, "ior")
@@ -364,9 +416,9 @@ class MATERIAL_PT_mirror(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
raym = active_node_mat(context.material).raytrace_mirror
@@ -422,9 +474,9 @@ class MATERIAL_PT_sss(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
mat = active_node_mat(context.material)
@@ -497,6 +549,7 @@ class MATERIAL_PT_halo(MaterialButtonsPanel, bpy.types.Panel):
col = split.column()
col.prop(mat, "alpha")
col.prop(mat, "diffuse_color", text="")
+ col.prop(halo, "seed")
col = split.column()
col.prop(halo, "size")
@@ -568,16 +621,20 @@ class MATERIAL_PT_physics(MaterialButtonsPanel, bpy.types.Panel):
phys = context.material.physics # dont use node material
split = layout.split()
+ row = split.row()
+ row.prop(phys, "friction")
+ row.prop(phys, "elasticity", slider=True)
- col = split.column()
- col.prop(phys, "distance")
- col.prop(phys, "friction")
- col.prop(phys, "use_normal_align")
+ row = layout.row()
+ row.label(text="Force Field:")
- col = split.column()
- col.prop(phys, "force", slider=True)
- col.prop(phys, "elasticity", slider=True)
- col.prop(phys, "damping", slider=True)
+ row = layout.row()
+ row.prop(phys, "fh_force")
+ row.prop(phys, "fh_damping", slider=True)
+
+ row = layout.row()
+ row.prop(phys, "fh_distance")
+ row.prop(phys, "use_fh_normal")
class MATERIAL_PT_strand(MaterialButtonsPanel, bpy.types.Panel):
@@ -589,7 +646,7 @@ class MATERIAL_PT_strand(MaterialButtonsPanel, bpy.types.Panel):
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE', 'HALO')) and (engine in cls.COMPAT_ENGINES)
+ return mat and (mat.type in {'SURFACE', 'WIRE', 'HALO'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
@@ -633,26 +690,29 @@ class MATERIAL_PT_options(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
- mat = active_node_mat(context.material)
+ base_mat = context.material
+ mat = active_node_mat(base_mat)
split = layout.split()
col = split.column()
- col.prop(mat, "use_raytrace")
- col.prop(mat, "use_full_oversampling")
- col.prop(mat, "use_sky")
+ if simple_material(base_mat):
+ col.prop(mat, "use_raytrace")
+ col.prop(mat, "use_full_oversampling")
+ col.prop(mat, "use_sky")
col.prop(mat, "use_mist")
- col.prop(mat, "invert_z")
- sub = col.row()
- sub.prop(mat, "offset_z")
- sub.active = mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY'
+ if simple_material(base_mat):
+ col.prop(mat, "invert_z")
+ sub = col.row()
+ sub.prop(mat, "offset_z")
+ sub.active = mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY'
sub = col.column(align=True)
sub.label(text="Light Group:")
sub.prop(mat, "light_group", text="")
@@ -678,26 +738,32 @@ class MATERIAL_PT_shadow(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (mat.type in ('SURFACE', 'WIRE')) and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
- mat = active_node_mat(context.material)
+ base_mat = context.material
+ mat = active_node_mat(base_mat)
split = layout.split()
col = split.column()
col.prop(mat, "use_shadows", text="Receive")
col.prop(mat, "use_transparent_shadows", text="Receive Transparent")
+ if simple_material(base_mat):
+ col.prop(mat, "use_cast_shadows_only", text="Cast Only")
+ col.prop(mat, "shadow_cast_alpha", text="Casting Alpha")
col.prop(mat, "use_only_shadow", text="Shadows Only")
- col.prop(mat, "use_cast_shadows_only", text="Cast Only")
- col.prop(mat, "shadow_cast_alpha", text="Casting Alpha")
+ sub = col.column()
+ sub.active = mat.use_only_shadow
+ sub.prop(mat, "shadow_only_type", text="")
col = split.column()
- col.prop(mat, "use_cast_buffer_shadows")
+ if simple_material(base_mat):
+ col.prop(mat, "use_cast_buffer_shadows")
sub = col.column()
sub.active = mat.use_cast_buffer_shadows
sub.prop(mat, "shadow_buffer_bias", text="Buffer Bias")
@@ -705,7 +771,8 @@ class MATERIAL_PT_shadow(MaterialButtonsPanel, bpy.types.Panel):
sub = col.column()
sub.active = (not mat.use_ray_shadow_bias)
sub.prop(mat, "shadow_ray_bias", text="Ray Bias")
- col.prop(mat, "use_cast_approximate")
+ if simple_material(base_mat):
+ col.prop(mat, "use_cast_approximate")
class MATERIAL_PT_transp_game(MaterialButtonsPanel, bpy.types.Panel):
@@ -715,29 +782,27 @@ class MATERIAL_PT_transp_game(MaterialButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- mat = active_node_mat(context.material)
+ mat = context.material
engine = context.scene.render.engine
- return mat and (engine in cls.COMPAT_ENGINES)
+ return check_material(mat) and (engine in cls.COMPAT_ENGINES)
def draw_header(self, context):
- mat = active_node_mat(context.material)
+ mat = context.material
- self.layout.prop(mat, "use_transparency", text="")
+ if simple_material(mat):
+ self.layout.prop(mat, "use_transparency", text="")
def draw(self, context):
layout = self.layout
+ base_mat = context.material
+ mat = active_node_mat(base_mat)
- mat = active_node_mat(context.material)
- rayt = mat.raytrace_transparency
-
- row = layout.row()
- row.active = mat.use_transparency and (not mat.use_shadeless)
- row.prop(mat, "transparency_method", expand=True)
-
- split = layout.split()
+ if simple_material(base_mat):
+ row = layout.row()
+ row.active = mat.use_transparency
+ row.prop(mat, "transparency_method", expand=True)
- col = split.column()
- col.prop(mat, "alpha")
+ layout.prop(mat, "alpha")
class VolumeButtonsPanel():
@@ -762,12 +827,9 @@ class MATERIAL_PT_volume_density(VolumeButtonsPanel, bpy.types.Panel):
vol = context.material.volume # dont use node material
- split = layout.split()
- col = split.column()
- col.prop(vol, "density")
-
- col = split.column()
- col.prop(vol, "density_scale")
+ row = layout.row()
+ row.prop(vol, "density")
+ row.prop(vol, "density_scale")
class MATERIAL_PT_volume_shading(VolumeButtonsPanel, bpy.types.Panel):
@@ -817,7 +879,7 @@ class MATERIAL_PT_volume_lighting(VolumeButtonsPanel, bpy.types.Panel):
sub = col.column()
sub.active = vol.use_light_cache
sub.prop(vol, "cache_resolution")
- elif vol.light_method in ('MULTIPLE_SCATTERING', 'SHADED_PLUS_MULTIPLE_SCATTERING'):
+ elif vol.light_method in {'MULTIPLE_SCATTERING', 'SHADED_PLUS_MULTIPLE_SCATTERING'}:
sub = col.column()
sub.enabled = True
sub.active = False
@@ -834,6 +896,12 @@ class MATERIAL_PT_volume_transp(VolumeButtonsPanel, bpy.types.Panel):
bl_label = "Transparency"
COMPAT_ENGINES = {'BLENDER_RENDER'}
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ engine = context.scene.render.engine
+ return mat and simple_material(mat) and (mat.type == 'VOLUME') and (engine in cls.COMPAT_ENGINES)
+
def draw(self, context):
layout = self.layout
@@ -869,6 +937,12 @@ class MATERIAL_PT_volume_options(VolumeButtonsPanel, bpy.types.Panel):
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
bl_options = {'DEFAULT_CLOSED'}
+ @classmethod
+ def poll(cls, context):
+ mat = context.material
+ engine = context.scene.render.engine
+ return check_material(mat) and (mat.type == 'VOLUME') and (engine in cls.COMPAT_ENGINES)
+
def draw(self, context):
layout = self.layout
@@ -877,8 +951,9 @@ class MATERIAL_PT_volume_options(VolumeButtonsPanel, bpy.types.Panel):
split = layout.split()
col = split.column()
- col.prop(mat, "use_raytrace")
- col.prop(mat, "use_full_oversampling")
+ if simple_material(context.material):
+ col.prop(mat, "use_raytrace")
+ col.prop(mat, "use_full_oversampling")
col.prop(mat, "use_mist")
col = split.column()
@@ -894,13 +969,5 @@ class MATERIAL_PT_custom_props(MaterialButtonsPanel, PropertyPanel, bpy.types.Pa
_context_path = "material"
_property_type = bpy.types.Material
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index 30380e2d338..ae66642e903 100644
--- a/release/scripts/ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -40,8 +40,7 @@ class OBJECT_PT_context_object(ObjectButtonsPanel, bpy.types.Panel):
layout.template_ID(space, "pin_id")
else:
row = layout.row()
- row.label(text="", icon='OBJECT_DATA')
- row.prop(ob, "name", text="")
+ row.template_ID(context.scene.objects, "active")
class OBJECT_PT_transform(ObjectButtonsPanel, bpy.types.Panel):
@@ -111,7 +110,7 @@ class OBJECT_PT_transform_locks(ObjectButtonsPanel, bpy.types.Panel):
col.prop(ob, "lock_location", text="Location")
col = row.column()
- if ob.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'):
+ if ob.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
col.prop(ob, "lock_rotations_4d", text="Rotation")
if ob.lock_rotations_4d:
col.prop(ob, "lock_rotation_w", text="W")
@@ -249,13 +248,10 @@ class OBJECT_PT_duplication(ObjectButtonsPanel, bpy.types.Panel):
layout.prop(ob, "use_dupli_vertices_rotation", text="Rotation")
elif ob.dupli_type == 'FACES':
- split = layout.split()
-
- col = split.column()
- col.prop(ob, "use_dupli_faces_scale", text="Scale")
- col = split.column()
- col.prop(ob, "dupli_faces_scale", text="Inherit Scale")
+ row = layout.row()
+ row.prop(ob, "use_dupli_faces_scale", text="Scale")
+ row.prop(ob, "dupli_faces_scale", text="Inherit Scale")
elif ob.dupli_type == 'GROUP':
layout.prop(ob, "dupli_group", text="Group")
@@ -291,7 +287,11 @@ class OBJECT_PT_animation(ObjectButtonsPanel, bpy.types.Panel):
col.prop(ob, "track_axis", text="Axis")
col.prop(ob, "up_axis", text="Up Axis")
-from properties_animviz import MotionPathButtonsPanel, OnionSkinButtonsPanel
+
+from bl_ui.properties_animviz import (
+ MotionPathButtonsPanel,
+ OnionSkinButtonsPanel,
+ )
class OBJECT_PT_motion_paths(MotionPathButtonsPanel, bpy.types.Panel):
@@ -311,13 +311,9 @@ class OBJECT_PT_motion_paths(MotionPathButtonsPanel, bpy.types.Panel):
layout.separator()
- split = layout.split()
-
- col = split.column()
- col.operator("object.paths_calculate", text="Calculate Paths")
-
- col = split.column()
- col.operator("object.paths_clear", text="Clear Paths")
+ row = layout.row()
+ row.operator("object.paths_calculate", text="Calculate Paths")
+ row.operator("object.paths_clear", text="Clear Paths")
class OBJECT_PT_onion_skinning(OnionSkinButtonsPanel): # , bpy.types.Panel): # inherit from panel when ready
@@ -329,8 +325,6 @@ class OBJECT_PT_onion_skinning(OnionSkinButtonsPanel): # , bpy.types.Panel): #
return (context.object)
def draw(self, context):
- layout = self.layout
-
ob = context.object
self.draw_settings(context, ob.animation_visualisation)
@@ -341,13 +335,5 @@ class OBJECT_PT_custom_props(ObjectButtonsPanel, PropertyPanel, bpy.types.Panel)
_context_path = "object"
_property_type = bpy.types.Object
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_object_constraint.py b/release/scripts/startup/bl_ui/properties_object_constraint.py
index 4b9c2885277..59f9ca16d1a 100644
--- a/release/scripts/ui/properties_object_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_object_constraint.py
@@ -34,7 +34,7 @@ class ConstraintButtonsPanel():
# match enum type to our functions, avoids a lookup table.
getattr(self, con.type)(context, box, con)
- if con.type not in ('RIGID_BODY_JOINT', 'NULL'):
+ if con.type not in {'RIGID_BODY_JOINT', 'NULL'}:
box.prop(con, "influence")
def space_template(self, layout, con, target=True, owner=True):
@@ -65,7 +65,7 @@ class ConstraintButtonsPanel():
row = layout.row()
row.label(text="Head/Tail:")
row.prop(con, "head_tail", text="")
- elif con.target.type in ('MESH', 'LATTICE'):
+ elif con.target.type in {'MESH', 'LATTICE'}:
layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group")
def ik_template(self, layout, con):
@@ -112,13 +112,9 @@ class ConstraintButtonsPanel():
col.prop(con, "use_scale_y", text="Y")
col.prop(con, "use_scale_z", text="Z")
- split = layout.split()
-
- col = split.column()
- col.operator("constraint.childof_set_inverse")
-
- col = split.column()
- col.operator("constraint.childof_clear_inverse")
+ row = layout.row()
+ row.operator("constraint.childof_set_inverse")
+ row.operator("constraint.childof_clear_inverse")
def TRACK_TO(self, context, layout, con):
self.target_template(layout, con)
@@ -127,13 +123,9 @@ class ConstraintButtonsPanel():
row.label(text="To:")
row.prop(con, "track_axis", expand=True)
- split = layout.split()
-
- col = split.column()
- col.prop(con, "up_axis", text="Up")
-
- col = split.column()
- col.prop(con, "use_target_z")
+ row = layout.row()
+ row.prop(con, "up_axis", text="Up")
+ row.prop(con, "use_target_z")
self.space_template(layout, con)
@@ -212,6 +204,7 @@ class ConstraintButtonsPanel():
self.ik_template(layout, con)
layout.prop(con, "limit_mode")
+
row = layout.row()
row.prop(con, "weight", text="Weight", slider=True)
row.prop(con, "distance", text="Distance", slider=True)
@@ -486,16 +479,11 @@ class ConstraintButtonsPanel():
def STRETCH_TO(self, context, layout, con):
self.target_template(layout, con)
- split = layout.split()
-
- col = split.column()
- col.prop(con, "rest_length", text="Rest Length")
-
- col = split.column()
- col.operator("constraint.stretchto_reset", text="Reset")
+ row = layout.row()
+ row.prop(con, "rest_length", text="Rest Length")
+ row.operator("constraint.stretchto_reset", text="Reset")
- col = layout.column()
- col.prop(con, "bulge", text="Volume Variation")
+ layout.prop(con, "bulge", text="Volume Variation")
row = layout.row()
row.label(text="Volume:")
@@ -507,13 +495,9 @@ class ConstraintButtonsPanel():
def FLOOR(self, context, layout, con):
self.target_template(layout, con)
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_sticky")
-
- col = split.column()
- col.prop(con, "use_rotation")
+ row = layout.row()
+ row.prop(con, "use_sticky")
+ row.prop(con, "use_rotation")
layout.prop(con, "offset")
@@ -529,13 +513,9 @@ class ConstraintButtonsPanel():
layout.prop(con, "pivot_type")
layout.prop(con, "child")
- split = layout.split()
-
- col = split.column()
- col.prop(con, "use_linked_collision", text="Linked Collision")
-
- col = split.column()
- col.prop(con, "show_pivot", text="Display Pivot")
+ row = layout.row()
+ row.prop(con, "use_linked_collision", text="Linked Collision")
+ row.prop(con, "show_pivot", text="Display Pivot")
split = layout.split()
@@ -555,15 +535,23 @@ class ConstraintButtonsPanel():
layout.label(text="Limits:")
split = layout.split()
- col = split.column(align=True)
- col.prop(con, "use_angular_limit_x", text="Angular X")
- col.prop(con, "use_angular_limit_y", text="Angular Y")
- col.prop(con, "use_angular_limit_z", text="Angular Z")
+ col = split.column()
+ col.prop(con, "use_angular_limit_x", text="Angle X")
+ sub = col.column()
+ sub.active = con.use_angular_limit_x
+ sub.prop(con, "limit_angle_max_x", text="")
col = split.column()
- col.prop(con, "limit_cone_min", text="")
+ col.prop(con, "use_angular_limit_y", text="Angle Y")
+ sub = col.column()
+ sub.active = con.use_angular_limit_y
+ sub.prop(con, "limit_angle_max_y", text="")
+
col = split.column()
- col.prop(con, "limit_cone_max", text="")
+ col.prop(con, "use_angular_limit_z", text="Angle Z")
+ sub = col.column()
+ sub.active = con.use_angular_limit_z
+ sub.prop(con, "limit_angle_max_z", text="")
elif con.pivot_type == 'GENERIC_6_DOF':
layout.label(text="Limits:")
@@ -571,16 +559,62 @@ class ConstraintButtonsPanel():
col = split.column(align=True)
col.prop(con, "use_limit_x", text="X")
+ sub = col.column()
+ sub.active = con.use_limit_x
+ sub.prop(con, "limit_min_x", text="Min")
+ sub.prop(con, "limit_max_x", text="Max")
+
+ col = split.column(align=True)
col.prop(con, "use_limit_y", text="Y")
+ sub = col.column()
+ sub.active = con.use_limit_y
+ sub.prop(con, "limit_min_y", text="Min")
+ sub.prop(con, "limit_max_y", text="Max")
+
+ col = split.column(align=True)
col.prop(con, "use_limit_z", text="Z")
- col.prop(con, "use_angular_limit_x", text="Angular X")
- col.prop(con, "use_angular_limit_y", text="Angular Y")
- col.prop(con, "use_angular_limit_z", text="Angular Z")
+ sub = col.column()
+ sub.active = con.use_limit_z
+ sub.prop(con, "limit_min_z", text="Min")
+ sub.prop(con, "limit_max_z", text="Max")
- col = split.column()
- col.prop(con, "limit_generic_min", text="")
- col = split.column()
- col.prop(con, "limit_generic_max", text="")
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.prop(con, "use_angular_limit_x", text="Angle X")
+ sub = col.column()
+ sub.active = con.use_angular_limit_x
+ sub.prop(con, "limit_angle_min_x", text="Min")
+ sub.prop(con, "limit_angle_max_x", text="Max")
+
+ col = split.column(align=True)
+ col.prop(con, "use_angular_limit_y", text="Angle Y")
+ sub = col.column()
+ sub.active = con.use_angular_limit_y
+ sub.prop(con, "limit_angle_min_y", text="Min")
+ sub.prop(con, "limit_angle_max_y", text="Max")
+
+ col = split.column(align=True)
+ col.prop(con, "use_angular_limit_z", text="Angle Z")
+ sub = col.column()
+ sub.active = con.use_angular_limit_z
+ sub.prop(con, "limit_angle_min_z", text="Min")
+ sub.prop(con, "limit_angle_max_z", text="Max")
+
+ elif con.pivot_type == 'HINGE':
+ layout.label(text="Limits:")
+ split = layout.split()
+
+ row = split.row(align=True)
+ col = row.column()
+ col.prop(con, "use_angular_limit_x", text="Angle X")
+
+ col = row.column()
+ col.active = con.use_angular_limit_x
+ col.prop(con, "limit_angle_min_x", text="Min")
+ col = row.column()
+ col.active = con.use_angular_limit_x
+ col.prop(con, "limit_angle_max_x", text="Max")
def CLAMP_TO(self, context, layout, con):
self.target_template(layout, con)
@@ -589,8 +623,7 @@ class ConstraintButtonsPanel():
row.label(text="Main Axis:")
row.prop(con, "main_axis", expand=True)
- row = layout.row()
- row.prop(con, "use_cyclic")
+ layout.prop(con, "use_cyclic")
def TRANSFORM(self, context, layout, con):
self.target_template(layout, con)
@@ -747,13 +780,5 @@ class BONE_PT_constraints(ConstraintButtonsPanel, bpy.types.Panel):
for con in context.pose_bone.constraints:
self.draw_constraint(context, con)
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index 4bed2e81629..5037308c6fd 100644
--- a/release/scripts/ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -20,15 +20,19 @@
import bpy
from rna_prop_ui import PropertyPanel
-from properties_physics_common import point_cache_ui
-from properties_physics_common import effector_weights_ui
-from properties_physics_common import basic_force_field_settings_ui
-from properties_physics_common import basic_force_field_falloff_ui
+from bl_ui.properties_physics_common import (
+ point_cache_ui,
+ effector_weights_ui,
+ basic_force_field_settings_ui,
+ basic_force_field_falloff_ui,
+ )
def particle_panel_enabled(context, psys):
+ if psys is None:
+ return True
phystype = psys.settings.physics_type
- if psys.settings.type in ('EMITTER', 'REACTOR') and phystype in ('NO', 'KEYED'):
+ if psys.settings.type in {'EMITTER', 'REACTOR'} and phystype in {'NO', 'KEYED'}:
return True
else:
return (psys.point_cache.is_baked is False) and (not psys.is_edited) and (not context.particle_system_editable)
@@ -37,11 +41,25 @@ def particle_panel_enabled(context, psys):
def particle_panel_poll(cls, context):
psys = context.particle_system
engine = context.scene.render.engine
- if psys is None:
- return False
- if psys.settings is None:
+ settings = 0
+
+ if psys:
+ settings = psys.settings
+ elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
+ settings = context.space_data.pin_id
+
+ if not settings:
return False
- return psys.settings.is_fluid == False and (engine in cls.COMPAT_ENGINES)
+
+ return settings.is_fluid == False and (engine in cls.COMPAT_ENGINES)
+
+
+def particle_get_settings(context):
+ if context.particle_system:
+ return context.particle_system.settings
+ elif isinstance(context.space_data.pin_id, bpy.types.ParticleSettings):
+ return context.space_data.pin_id
+ return None
class ParticleButtonsPanel():
@@ -62,13 +80,14 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
engine = context.scene.render.engine
- return (context.particle_system or context.object) and (engine in cls.COMPAT_ENGINES)
+ return (context.particle_system or context.object or context.space_data.pin_id) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
ob = context.object
psys = context.particle_system
+ part = 0
if ob:
row = layout.row()
@@ -79,7 +98,21 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, bpy.types.Panel):
col.operator("object.particle_system_add", icon='ZOOMIN', text="")
col.operator("object.particle_system_remove", icon='ZOOMOUT', text="")
- if psys and not psys.settings:
+ if psys is None:
+ part = particle_get_settings(context)
+
+ if part is None:
+ return
+
+ layout.template_ID(context.space_data, "pin_id")
+
+ if part.is_fluid:
+ layout.label(text="Settings used for fluid.")
+ return
+
+ layout.prop(part, "type", text="Type")
+
+ elif not psys.settings:
split = layout.split(percentage=0.32)
col = split.column()
@@ -89,7 +122,7 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, bpy.types.Panel):
col = split.column()
col.prop(psys, "name", text="")
col.template_ID(psys, "settings", new="particle.new")
- elif psys:
+ else:
part = psys.settings
split = layout.split(percentage=0.32)
@@ -110,39 +143,39 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel, bpy.types.Panel):
#row.label(text="Viewport")
#row.label(text="Render")
- if part:
- if part.is_fluid:
- layout.label(text=str(part.count) + " fluid particles for this frame.")
- return
+ if part.is_fluid:
+ layout.label(text=str(part.count) + " fluid particles for this frame.")
+ return
- row = col.row()
- row.enabled = particle_panel_enabled(context, psys)
- row.prop(part, "type", text="")
- row.prop(psys, "seed")
+ row = col.row()
+ row.enabled = particle_panel_enabled(context, psys)
+ row.prop(part, "type", text="")
+ row.prop(psys, "seed")
- split = layout.split(percentage=0.65)
- if part.type == 'HAIR':
- if psys.is_edited:
- split.operator("particle.edited_clear", text="Free Edit")
- else:
- row = split.row()
- row.enabled = particle_panel_enabled(context, psys)
- row.prop(part, "regrow_hair")
- row.prop(part, "use_advanced_hair")
+ if part:
+ split = layout.split(percentage=0.65)
+ if part.type == 'HAIR':
+ if psys != None and psys.is_edited:
+ split.operator("particle.edited_clear", text="Free Edit")
+ else:
row = split.row()
row.enabled = particle_panel_enabled(context, psys)
- row.prop(part, "hair_step")
- if 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 part.type == 'REACTOR':
- split.enabled = particle_panel_enabled(context, psys)
- split.prop(psys, "reactor_target_object")
- split.prop(psys, "reactor_target_particle_system", text="Particle System")
+ row.prop(part, "regrow_hair")
+ row.prop(part, "use_advanced_hair")
+ row = split.row()
+ row.enabled = particle_panel_enabled(context, psys)
+ row.prop(part, "hair_step")
+ 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")
+ split.prop(psys, "reactor_target_particle_system", text="Particle System")
class PARTICLE_PT_emission(ParticleButtonsPanel, bpy.types.Panel):
@@ -152,23 +185,23 @@ class PARTICLE_PT_emission(ParticleButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
psys = context.particle_system
- if psys is None:
- return False
- if psys.settings is None:
+ settings = particle_get_settings(context)
+
+ if settings is None:
return False
- if psys.settings.is_fluid:
+ if settings.is_fluid:
return False
if particle_panel_poll(PARTICLE_PT_emission, context):
- return not context.particle_system.point_cache.use_external
+ return psys is None or not context.particle_system.point_cache.use_external
return False
def draw(self, context):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ part = particle_get_settings(context)
- layout.enabled = particle_panel_enabled(context, psys) and not psys.has_multiple_caches
+ layout.enabled = particle_panel_enabled(context, psys) and (psys is None or not psys.has_multiple_caches)
row = layout.row()
row.active = part.distribution != 'GRID'
@@ -189,25 +222,23 @@ class PARTICLE_PT_emission(ParticleButtonsPanel, bpy.types.Panel):
col.prop(part, "lifetime")
col.prop(part, "lifetime_random", slider=True)
- layout.row().label(text="Emit From:")
-
- row = layout.row()
- row.prop(part, "emit_from", expand=True)
+ layout.label(text="Emit From:")
+ layout.prop(part, "emit_from", expand=True)
row = layout.row()
- if part.distribution == 'GRID':
+ if part.emit_from == 'VERT':
+ row.prop(part, "use_emit_random")
+ elif part.distribution == 'GRID':
row.prop(part, "invert_grid")
+ row.prop(part, "hexagonal_grid")
else:
row.prop(part, "use_emit_random")
row.prop(part, "use_even_distribution")
if part.emit_from == 'FACE' or part.emit_from == 'VOLUME':
- row = layout.row()
-
- row.prop(part, "distribution", expand=True)
+ layout.prop(part, "distribution", expand=True)
row = layout.row()
-
if part.distribution == 'JIT':
row.prop(part, "userjit", text="Particles/Face")
row.prop(part, "jitter_factor", text="Jittering Amount", slider=True)
@@ -247,7 +278,6 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, bpy.types.Panel):
if not psys.cloth:
return
- #part = psys.settings
cloth = psys.cloth.settings
layout.enabled = psys.use_hair_dynamics and psys.point_cache.is_baked == False
@@ -264,7 +294,6 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, bpy.types.Panel):
sub.prop(cloth, "collider_friction", slider=True)
col = split.column()
-
col.label(text="Damping:")
sub = col.column(align=True)
sub.prop(cloth, "spring_damping", text="Spring")
@@ -292,7 +321,7 @@ class PARTICLE_PT_cache(ParticleButtonsPanel, bpy.types.Panel):
phystype = psys.settings.physics_type
if phystype == 'NO' or phystype == 'KEYED':
return False
- return (psys.settings.type in ('EMITTER', 'REACTOR') or (psys.settings.type == 'HAIR' and (psys.use_hair_dynamics or psys.point_cache.is_baked))) and engine in cls.COMPAT_ENGINES
+ return (psys.settings.type in {'EMITTER', 'REACTOR'} or (psys.settings.type == 'HAIR' and (psys.use_hair_dynamics or psys.point_cache.is_baked))) and engine in cls.COMPAT_ENGINES
def draw(self, context):
psys = context.particle_system
@@ -308,9 +337,11 @@ class PARTICLE_PT_velocity(ParticleButtonsPanel, bpy.types.Panel):
def poll(cls, context):
if particle_panel_poll(PARTICLE_PT_velocity, context):
psys = context.particle_system
- if psys.settings.type == 'HAIR' and not psys.settings.use_advanced_hair:
+ settings = particle_get_settings(context)
+
+ if settings.type == 'HAIR' and not settings.use_advanced_hair:
return False
- return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.use_external
+ return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
else:
return False
@@ -318,32 +349,30 @@ class PARTICLE_PT_velocity(ParticleButtonsPanel, bpy.types.Panel):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ part = particle_get_settings(context)
layout.enabled = particle_panel_enabled(context, psys)
split = layout.split()
- sub = split.column()
- sub.label(text="Emitter Geometry:")
- sub.prop(part, "normal_factor")
- subsub = sub.column(align=True)
- subsub.prop(part, "tangent_factor")
- subsub.prop(part, "tangent_phase", slider=True)
+ col = split.column()
+ col.label(text="Emitter Geometry:")
+ col.prop(part, "normal_factor")
+ sub = col.column(align=True)
+ sub.prop(part, "tangent_factor")
+ sub.prop(part, "tangent_phase", slider=True)
- sub = split.column()
- sub.label(text="Emitter Object")
- sub.prop(part, "object_align_factor", text="")
+ col = split.column()
+ col.label(text="Emitter Object:")
+ col.prop(part, "object_align_factor", text="")
- layout.row().label(text="Other:")
- split = layout.split()
- sub = split.column()
+ layout.label(text="Other:")
+ row = layout.row()
if part.emit_from == 'PARTICLE':
- sub.prop(part, "particle_factor")
+ row.prop(part, "particle_factor")
else:
- sub.prop(part, "object_factor", slider=True)
- sub = split.column()
- sub.prop(part, "factor_random")
+ row.prop(part, "object_factor", slider=True)
+ row.prop(part, "factor_random")
#if part.type=='REACTOR':
# sub.prop(part, "reactor_factor")
@@ -358,9 +387,11 @@ class PARTICLE_PT_rotation(ParticleButtonsPanel, bpy.types.Panel):
def poll(cls, context):
if particle_panel_poll(PARTICLE_PT_rotation, context):
psys = context.particle_system
- if psys.settings.type == 'HAIR' and not psys.settings.use_advanced_hair:
+ settings = particle_get_settings(context)
+
+ if settings.type == 'HAIR' and not settings.use_advanced_hair:
return False
- return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.use_external
+ return settings.physics_type != 'BOIDS' and (psys is None or not psys.point_cache.use_external)
else:
return False
@@ -368,31 +399,33 @@ class PARTICLE_PT_rotation(ParticleButtonsPanel, bpy.types.Panel):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ if psys:
+ part = psys.settings
+ else:
+ part = context.space_data.pin_id
layout.enabled = particle_panel_enabled(context, psys)
- split = layout.split()
- split.label(text="Initial Rotation:")
- split.prop(part, "use_dynamic_rotation")
- split = layout.split()
+ row = layout.row()
+ row.label(text="Initial Rotation:")
+ row.prop(part, "use_dynamic_rotation")
- sub = split.column(align=True)
- sub.prop(part, "rotation_mode", text="")
- sub.prop(part, "rotation_factor_random", slider=True, text="Random")
+ split = layout.split()
- sub = split.column(align=True)
- sub.prop(part, "phase_factor", slider=True)
- sub.prop(part, "phase_factor_random", text="Random", slider=True)
+ col = split.column(align=True)
+ col.prop(part, "rotation_mode", text="")
+ col.prop(part, "rotation_factor_random", slider=True, text="Random")
- layout.row().label(text="Angular Velocity:")
- layout.row().prop(part, "angular_velocity_mode", expand=True)
- split = layout.split()
+ col = split.column(align=True)
+ col.prop(part, "phase_factor", slider=True)
+ col.prop(part, "phase_factor_random", text="Random", slider=True)
- sub = split.column()
+ col = layout.column()
+ col.label(text="Angular Velocity:")
+ col.row().prop(part, "angular_velocity_mode", expand=True)
if part.angular_velocity_mode != 'NONE':
- sub.prop(part, "angular_velocity_factor", text="")
+ col.prop(part, "angular_velocity_factor", text="")
class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
@@ -403,9 +436,11 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
def poll(cls, context):
if particle_panel_poll(PARTICLE_PT_physics, context):
psys = context.particle_system
- if psys.settings.type == 'HAIR' and not psys.settings.use_advanced_hair:
+ settings = particle_get_settings(context)
+
+ if settings.type == 'HAIR' and not settings.use_advanced_hair:
return False
- return not psys.point_cache.use_external
+ return psys is None or not psys.point_cache.use_external
else:
return False
@@ -413,12 +448,11 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ part = particle_get_settings(context)
layout.enabled = particle_panel_enabled(context, psys)
- row = layout.row()
- row.prop(part, "physics_type", expand=True)
+ layout.prop(part, "physics_type", expand=True)
row = layout.row()
col = row.column(align=True)
@@ -430,70 +464,76 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
col.prop(part, "mass")
col.prop(part, "use_multiply_size_mass", text="Multiply mass with size")
- if part.physics_type == 'NEWTON':
+ if part.physics_type in ('NEWTON', 'FLUID'):
split = layout.split()
- sub = split.column()
- sub.label(text="Forces:")
- sub.prop(part, "brownian_factor")
- sub.prop(part, "drag_factor", slider=True)
- sub.prop(part, "damping", slider=True)
- sub = split.column()
- sub.label(text="Integration:")
- sub.prop(part, "integrator", text="")
- sub.prop(part, "time_tweak")
- sub.prop(part, "subframes")
- sub = layout.row()
- sub.prop(part, "use_size_deflect")
- sub.prop(part, "use_die_on_collision")
-
- elif part.physics_type == 'FLUID':
- fluid = part.fluid
- split = layout.split()
- sub = split.column()
+ col = split.column()
+ col.label(text="Forces:")
+ col.prop(part, "brownian_factor")
+ col.prop(part, "drag_factor", slider=True)
+ col.prop(part, "damping", slider=True)
- sub.label(text="Forces:")
- sub.prop(part, "brownian_factor")
- sub.prop(part, "drag_factor", slider=True)
- sub.prop(part, "damping", slider=True)
- sub = split.column()
- sub.label(text="Integration:")
- sub.prop(part, "integrator", text="")
- sub.prop(part, "time_tweak")
- sub.prop(part, "subframes")
- sub = layout.row()
- sub.prop(part, "use_size_deflect")
- sub.prop(part, "use_die_on_collision")
+ col = split.column()
+ col.label(text="Integration:")
+ col.prop(part, "integrator", text="")
+ col.prop(part, "timestep")
+ col.prop(part, "subframes")
- split = layout.split()
- sub = split.column()
- sub.label(text="Fluid Interaction:")
- sub.prop(fluid, "fluid_radius")
- sub.prop(fluid, "repulsion_force")
- subsub = sub.column(align=True)
- subsub.prop(fluid, "rest_density")
- subsub.prop(fluid, "density_force", text="Force")
+ row = layout.row()
+ row.prop(part, "use_size_deflect")
+ row.prop(part, "use_die_on_collision")
- sub.label(text="Viscosity:")
- subsub = sub.column(align=True)
- subsub.prop(fluid, "linear_viscosity", text="Linear")
- subsub.prop(fluid, "square_viscosity", text="Square")
+ if part.physics_type == 'FLUID':
+ fluid = part.fluid
- sub = split.column()
+ split = layout.split()
- sub.label(text="Springs:")
- sub.prop(fluid, "spring_force", text="Force")
- #Hidden to make ui a bit lighter, can be unhidden for a bit more control
- #sub.prop(fluid, "rest_length", slider=True)
- sub.prop(fluid, "use_viscoelastic_springs")
- subsub = sub.column(align=True)
- subsub.active = fluid.use_viscoelastic_springs
- subsub.prop(fluid, "yield_ratio", slider=True)
- subsub.prop(fluid, "plasticity", slider=True)
- subsub.prop(fluid, "use_initial_rest_length")
+ col = split.column()
+ col.label(text="Fluid properties:")
+ col.prop(fluid, "stiffness", text="Stiffness")
+ col.prop(fluid, "linear_viscosity", text="Viscosity")
+ col.prop(fluid, "buoyancy", text="Buoancy", slider=True)
- sub.label(text="Buoyancy:")
- sub.prop(fluid, "buoyancy", text="Strength", slider=True)
+ col = split.column()
+ col.label(text="Advanced:")
+
+ sub = col.row()
+ sub.prop(fluid, "repulsion", slider=fluid.factor_repulsion)
+ sub.prop(fluid, "factor_repulsion", text="")
+
+ sub = col.row()
+ sub.prop(fluid, "stiff_viscosity", slider=fluid.factor_stiff_viscosity)
+ sub.prop(fluid, "factor_stiff_viscosity", text="")
+
+ sub = col.row()
+ sub.prop(fluid, "fluid_radius", slider=fluid.factor_radius)
+ sub.prop(fluid, "factor_radius", text="")
+
+ sub = col.row()
+ sub.prop(fluid, "rest_density", slider=fluid.factor_density)
+ sub.prop(fluid, "factor_density", text="")
+
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="Springs:")
+ col.prop(fluid, "spring_force", text="Force")
+ col.prop(fluid, "use_viscoelastic_springs")
+ sub = col.column(align=True)
+ sub.active = fluid.use_viscoelastic_springs
+ sub.prop(fluid, "yield_ratio", slider=True)
+ sub.prop(fluid, "plasticity", slider=True)
+
+ col = split.column()
+ col.label(text="Advanced:")
+ sub = col.row()
+ sub.prop(fluid, "rest_length", slider=fluid.factor_rest_length)
+ sub.prop(fluid, "factor_rest_length", text="")
+ col.label(text="")
+ sub = col.column()
+ sub.active = fluid.use_viscoelastic_springs
+ sub.prop(fluid, "use_initial_rest_length")
+ sub.prop(fluid, "spring_frames", text="Frames")
elif part.physics_type == 'KEYED':
split = layout.split()
@@ -503,7 +543,8 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
col = row.column()
col.active = not psys.use_keyed_timing
col.prop(part, "keyed_loops", text="Loops")
- row.prop(psys, "use_keyed_timing", text="Use Timing")
+ if psys:
+ row.prop(psys, "use_keyed_timing", text="Use Timing")
layout.label(text="Keys:")
elif part.physics_type == 'BOIDS':
@@ -554,7 +595,7 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, bpy.types.Panel):
col.prop(boids, "pitch", slider=True)
col.prop(boids, "height", slider=True)
- if part.physics_type == 'KEYED' or part.physics_type == 'BOIDS' or part.physics_type == 'FLUID':
+ if psys and part.physics_type in {'KEYED', 'BOIDS', 'FLUID'}:
if part.physics_type == 'BOIDS':
layout.label(text="Relations:")
elif part.physics_type == 'FLUID':
@@ -609,19 +650,19 @@ class PARTICLE_PT_boidbrain(ParticleButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
psys = context.particle_system
+ settings = particle_get_settings(context)
engine = context.scene.render.engine
- if psys is None:
- return False
- if psys.settings is None:
+
+ if settings is None:
return False
- if psys.point_cache.use_external:
+ if psys != None and psys.point_cache.use_external:
return False
- return psys.settings.physics_type == 'BOIDS' and engine in cls.COMPAT_ENGINES
+ return settings.physics_type == 'BOIDS' and engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
- boids = context.particle_system.settings.boids
+ boids = particle_get_settings(context).boids
layout.enabled = particle_panel_enabled(context, context.particle_system)
@@ -709,65 +750,65 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- psys = context.particle_system
+ settings = particle_get_settings(context)
engine = context.scene.render.engine
- if psys is None:
- return False
- if psys.settings is None:
+ if settings is None:
return False
+
return engine in cls.COMPAT_ENGINES
def draw(self, context):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ part = particle_get_settings(context)
row = layout.row()
row.prop(part, "material")
- row.prop(psys, "parent")
+ if psys:
+ row.prop(psys, "parent")
split = layout.split()
- sub = split.column()
- sub.prop(part, "use_render_emitter")
- sub.prop(part, "use_parent_particles")
- sub = split.column()
- sub.prop(part, "show_unborn")
- sub.prop(part, "use_dead")
+ col = split.column()
+ col.prop(part, "use_render_emitter")
+ col.prop(part, "use_parent_particles")
- row = layout.row()
- row.prop(part, "render_type", expand=True)
+ col = split.column()
+ col.prop(part, "show_unborn")
+ col.prop(part, "use_dead")
+
+ layout.prop(part, "render_type", expand=True)
split = layout.split()
- sub = split.column()
+ col = split.column()
if part.render_type == 'LINE':
- sub.prop(part, "line_length_tail")
- sub.prop(part, "line_length_head")
- sub = split.column()
- sub.prop(part, "use_velocity_length")
+ col.prop(part, "line_length_tail")
+ col.prop(part, "line_length_head")
+
+ split.prop(part, "use_velocity_length")
elif part.render_type == 'PATH':
- sub.prop(part, "use_strand_primitive")
- subsub = sub.column()
- subsub.active = (part.use_strand_primitive is False)
- subsub.prop(part, "use_render_adaptive")
- subsub = sub.column()
- subsub.active = part.use_render_adaptive or part.use_strand_primitive == True
- subsub.prop(part, "adaptive_angle")
- subsub = sub.column()
- subsub.active = (part.use_render_adaptive is True and part.use_strand_primitive is False)
- subsub.prop(part, "adaptive_pixel")
- sub.prop(part, "use_hair_bspline")
- sub.prop(part, "render_step", text="Steps")
+ col.prop(part, "use_strand_primitive")
+ sub = col.column()
+ sub.active = (part.use_strand_primitive is False)
+ sub.prop(part, "use_render_adaptive")
+ sub = col.column()
+ sub.active = part.use_render_adaptive or part.use_strand_primitive == True
+ sub.prop(part, "adaptive_angle")
+ sub = col.column()
+ sub.active = (part.use_render_adaptive is True and part.use_strand_primitive is False)
+ sub.prop(part, "adaptive_pixel")
+ col.prop(part, "use_hair_bspline")
+ col.prop(part, "render_step", text="Steps")
- sub = split.column()
- sub.label(text="Timing:")
- sub.prop(part, "use_absolute_path_time")
- sub.prop(part, "path_start", text="Start", slider=not part.use_absolute_path_time)
- sub.prop(part, "path_end", text="End", slider=not part.use_absolute_path_time)
- sub.prop(part, "length_random", text="Random", slider=True)
+ col = split.column()
+ col.label(text="Timing:")
+ col.prop(part, "use_absolute_path_time")
+ col.prop(part, "path_start", text="Start", slider=not part.use_absolute_path_time)
+ col.prop(part, "path_end", text="End", slider=not part.use_absolute_path_time)
+ col.prop(part, "length_random", text="Random", slider=True)
row = layout.row()
col = row.column()
@@ -786,22 +827,23 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel):
sub.prop(part, "simplify_viewport")
elif part.render_type == 'OBJECT':
- sub.prop(part, "dupli_object")
- sub.prop(part, "use_global_dupli")
+ col.prop(part, "dupli_object")
+ col.prop(part, "use_global_dupli")
elif part.render_type == 'GROUP':
- sub.prop(part, "dupli_group")
+ col.prop(part, "dupli_group")
split = layout.split()
- sub = split.column()
- sub.prop(part, "use_whole_group")
- subsub = sub.column()
- subsub.active = (part.use_whole_group is False)
- subsub.prop(part, "use_group_count")
- sub = split.column()
- subsub = sub.column()
- subsub.active = (part.use_whole_group is False)
- subsub.prop(part, "use_global_dupli")
- subsub.prop(part, "use_group_pick_random")
+ col = split.column()
+ col.prop(part, "use_whole_group")
+ sub = col.column()
+ sub.active = (part.use_whole_group is False)
+ sub.prop(part, "use_group_count")
+
+ col = split.column()
+ sub = col.column()
+ sub.active = (part.use_whole_group is False)
+ sub.prop(part, "use_global_dupli")
+ sub.prop(part, "use_group_pick_random")
if part.use_group_count and not part.use_whole_group:
row = layout.row()
@@ -821,7 +863,9 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel):
row.prop(weight, "count")
elif part.render_type == 'BILLBOARD':
- sub.label(text="Align:")
+ ob = context.object
+
+ col.label(text="Align:")
row = layout.row()
row.prop(part, "billboard_align", expand=True)
@@ -833,21 +877,25 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel):
col = row.column(align=True)
col.label(text="Tilt:")
col.prop(part, "billboard_tilt", text="Angle", slider=True)
- col.prop(part, "billboard_tilt_random", slider=True)
+ col.prop(part, "billboard_tilt_random", text="Random", slider=True)
col = row.column()
col.prop(part, "billboard_offset")
- row = layout.row()
- row.prop(psys, "billboard_normal_uv")
- row = layout.row()
- row.prop(psys, "billboard_time_index_uv")
+ if psys:
+ col = layout.column()
+ col.prop_search(psys, "billboard_normal_uv", ob.data, "uv_textures")
+ col.prop_search(psys, "billboard_time_index_uv", ob.data, "uv_textures")
- row = layout.row()
- row.label(text="Split uv's:")
- row.prop(part, "billboard_uv_split", text="Number of splits")
- row = layout.row()
- row.prop(psys, "billboard_split_uv")
- row = layout.row()
+ split = layout.split(percentage=0.33)
+ split.label(text="Split uv's:")
+ split.prop(part, "billboard_uv_split", text="Number of splits")
+
+ if psys:
+ col = layout.column()
+ col.active = part.billboard_uv_split > 1
+ col.prop_search(psys, "billboard_split_uv", ob.data, "uv_textures")
+
+ row = col.row()
row.label(text="Animate:")
row.prop(part, "billboard_animation", text="")
row.label(text="Offset:")
@@ -866,6 +914,11 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel):
col = row.column()
col.label(text="")
+ if part.render_type in ('OBJECT', 'GROUP') and not part.use_advanced_hair:
+ row = layout.row(align=True)
+ row.prop(part, "particle_size")
+ row.prop(part, "size_random", slider=True)
+
class PARTICLE_PT_draw(ParticleButtonsPanel, bpy.types.Panel):
bl_label = "Display"
@@ -874,11 +927,9 @@ class PARTICLE_PT_draw(ParticleButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
- psys = context.particle_system
+ settings = particle_get_settings(context)
engine = context.scene.render.engine
- if psys is None:
- return False
- if psys.settings is None:
+ if settings is None:
return False
return engine in cls.COMPAT_ENGINES
@@ -886,7 +937,7 @@ class PARTICLE_PT_draw(ParticleButtonsPanel, bpy.types.Panel):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ part = particle_get_settings(context)
row = layout.row()
row.prop(part, "draw_method", expand=True)
@@ -903,7 +954,7 @@ class PARTICLE_PT_draw(ParticleButtonsPanel, bpy.types.Panel):
else:
row.label(text="")
- if part.draw_percentage != 100:
+ if part.draw_percentage != 100 and psys is not None:
if part.type == 'HAIR':
if psys.use_hair_dynamics and psys.point_cache.is_baked == False:
layout.row().label(text="Display percentage makes dynamics inaccurate without baking!")
@@ -920,16 +971,15 @@ class PARTICLE_PT_draw(ParticleButtonsPanel, bpy.types.Panel):
if part.physics_type == 'BOIDS':
col.prop(part, "show_health")
- col = row.column()
- col.prop(part, "show_material_color", text="Use material color")
+ col = row.column(align=True)
+ col.label(text="Color:")
+ col.prop(part, "draw_color", text="")
+ sub = col.row()
+ sub.active = part.draw_color in ('VELOCITY', 'ACCELERATION')
+ sub.prop(part, "color_maximum", text="Max")
if (path):
col.prop(part, "draw_step")
- else:
- sub = col.column()
- sub.active = (part.show_material_color is False)
- #sub.label(text="color")
- #sub.label(text="Override material color")
class PARTICLE_PT_children(ParticleButtonsPanel, bpy.types.Panel):
@@ -945,7 +995,7 @@ class PARTICLE_PT_children(ParticleButtonsPanel, bpy.types.Panel):
layout = self.layout
psys = context.particle_system
- part = psys.settings
+ part = particle_get_settings(context)
layout.row().prop(part, "child_type", expand=True)
@@ -960,7 +1010,8 @@ class PARTICLE_PT_children(ParticleButtonsPanel, bpy.types.Panel):
if part.child_type == 'INTERPOLATED':
col = row.column()
- col.prop(psys, "child_seed", text="Seed")
+ if psys:
+ col.prop(psys, "child_seed", text="Seed")
col.prop(part, "virtual_parents", slider=True)
col.prop(part, "create_long_hair_children")
else:
@@ -985,7 +1036,8 @@ class PARTICLE_PT_children(ParticleButtonsPanel, bpy.types.Panel):
sub = col.column(align=True)
sub.prop(part, "child_radius", text="Radius")
sub.prop(part, "child_roundness", text="Roundness", slider=True)
- sub.prop(psys, "child_seed", text="Seed")
+ if psys:
+ sub.prop(psys, "child_seed", text="Seed")
elif part.virtual_parents > 0.0:
sub = col.column(align=True)
sub.label(text="Parting not")
@@ -1040,7 +1092,7 @@ class PARTICLE_PT_field_weights(ParticleButtonsPanel, bpy.types.Panel):
return particle_panel_poll(cls, context)
def draw(self, context):
- part = context.particle_system.settings
+ part = particle_get_settings(context)
effector_weights_ui(self, context, part.effector_weights)
if part.type == 'HAIR':
@@ -1059,14 +1111,18 @@ class PARTICLE_PT_force_fields(ParticleButtonsPanel, bpy.types.Panel):
def draw(self, context):
layout = self.layout
- part = context.particle_system.settings
+ part = particle_get_settings(context)
- layout.prop(part, "use_self_effect")
+ row = layout.row()
+ row.prop(part, "use_self_effect")
+ row.prop(part, "effector_amount", text="Amount")
split = layout.split(percentage=0.2)
split.label(text="Type 1:")
split.prop(part.force_field_1, "type", text="")
basic_force_field_settings_ui(self, context, part.force_field_1)
+ if part.force_field_1.type != 'NONE':
+ layout.label(text="Falloff:")
basic_force_field_falloff_ui(self, context, part.force_field_1)
if part.force_field_1.type != 'NONE':
@@ -1076,6 +1132,8 @@ class PARTICLE_PT_force_fields(ParticleButtonsPanel, bpy.types.Panel):
split.label(text="Type 2:")
split.prop(part.force_field_2, "type", text="")
basic_force_field_settings_ui(self, context, part.force_field_2)
+ if part.force_field_2.type != 'NONE':
+ layout.label(text="Falloff:")
basic_force_field_falloff_ui(self, context, part.force_field_2)
@@ -1086,6 +1144,8 @@ class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
+ if context.particle_system is None:
+ return False
return particle_panel_poll(cls, context)
def draw(self, context):
@@ -1093,9 +1153,6 @@ class PARTICLE_PT_vertexgroups(ParticleButtonsPanel, bpy.types.Panel):
ob = context.object
psys = context.particle_system
- # part = psys.settings
-
- # layout.label(text="Nothing here yet.")
row = layout.row()
row.label(text="Vertex Group")
@@ -1156,13 +1213,5 @@ class PARTICLE_PT_custom_props(ParticleButtonsPanel, PropertyPanel, bpy.types.Pa
_context_path = "particle_system.settings"
_property_type = bpy.types.ParticleSettings
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_physics_cloth.py b/release/scripts/startup/bl_ui/properties_physics_cloth.py
index 0ac4429c71a..bce6ab993a7 100644
--- a/release/scripts/ui/properties_physics_cloth.py
+++ b/release/scripts/startup/bl_ui/properties_physics_cloth.py
@@ -20,8 +20,10 @@
import bpy
-from properties_physics_common import point_cache_ui
-from properties_physics_common import effector_weights_ui
+from bl_ui.properties_physics_common import (
+ point_cache_ui,
+ effector_weights_ui,
+ )
def cloth_panel_enabled(md):
@@ -112,7 +114,7 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel, bpy.types.Panel):
if key:
col.label(text="Rest Shape Key:")
- col.prop_search(cloth, "rest_shape_key", key, "keys", text="")
+ col.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="")
class PHYSICS_PT_cloth_cache(PhysicButtonsPanel, bpy.types.Panel):
@@ -155,6 +157,8 @@ class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, bpy.types.Panel):
col = split.column()
col.prop(cloth, "collision_quality", slider=True, text="Quality")
col.prop(cloth, "distance_min", slider=True, text="Distance")
+ col.prop(cloth, "repel_force", slider=True, text="Repel")
+ col.prop(cloth, "distance_repel", slider=True, text="Repel Distance")
col.prop(cloth, "friction")
col = split.column()
@@ -215,13 +219,5 @@ class PHYSICS_PT_cloth_field_weights(PhysicButtonsPanel, bpy.types.Panel):
cloth = context.cloth.settings
effector_weights_ui(self, context, cloth.effector_weights)
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 3d854533506..f7cf8da1840 100644
--- a/release/scripts/ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -89,7 +89,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
col.operator("ptcache.remove", icon='ZOOMOUT', text="")
row = layout.row()
- if cachetype in ('PSYS', 'HAIR', 'SMOKE'):
+ if cachetype in {'PSYS', 'HAIR', 'SMOKE'}:
row.prop(cache, "use_external")
if cache.use_external:
@@ -106,7 +106,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
layout.label(text=cache.info)
else:
if cachetype == 'SMOKE':
- if bpy.data.is_dirty:
+ if not bpy.data.is_saved:
layout.label(text="Cache is disabled until the file is saved")
layout.enabled = False
@@ -121,7 +121,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
row.enabled = enabled
row.prop(cache, "frame_start")
row.prop(cache, "frame_end")
- if cachetype not in ('SMOKE', 'CLOTH'):
+ if cachetype not in {'SMOKE', 'CLOTH'}:
row.prop(cache, "frame_step")
row.prop(cache, "use_quick_cache")
if cachetype != 'SMOKE':
@@ -129,7 +129,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
if cachetype != 'SMOKE':
split = layout.split()
- split.enabled = enabled and (not bpy.data.is_dirty)
+ split.enabled = enabled and bpy.data.is_saved
col = split.column()
col.prop(cache, "use_disk_cache")
@@ -139,7 +139,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype):
col.prop(cache, "use_library_path", "Use Lib Path")
row = layout.row()
- row.enabled = enabled and (not bpy.data.is_dirty)
+ row.enabled = enabled and bpy.data.is_saved
row.active = cache.use_disk_cache
row.label(text="Compression:")
row.prop(cache, "compression", expand=True)
@@ -232,8 +232,9 @@ def basic_force_field_settings_ui(self, context, field):
col.prop(field, "flow")
col = split.column()
- col.prop(field, "noise")
- col.prop(field, "seed")
+ sub = col.column(align=True)
+ sub.prop(field, "noise")
+ sub.prop(field, "seed")
if field.type == 'TURBULENCE':
col.prop(field, "use_global_coords", text="Global")
elif field.type == 'HARMONIC':
@@ -254,8 +255,6 @@ def basic_force_field_settings_ui(self, context, field):
def basic_force_field_falloff_ui(self, context, field):
layout = self.layout
- # XXX: This doesn't update for some reason.
- #split = layout.split()
split = layout.split(percentage=0.35)
if not field or field.type == 'NONE':
@@ -263,27 +262,24 @@ def basic_force_field_falloff_ui(self, context, field):
col = split.column()
col.prop(field, "z_direction", text="")
- col.prop(field, "use_min_distance", text="Use Minimum")
- col.prop(field, "use_max_distance", text="Use Maximum")
col = split.column()
col.prop(field, "falloff_power", text="Power")
- sub = col.column()
+ split = layout.split()
+ col = split.column()
+ row = col.row(align=True)
+ row.prop(field, "use_min_distance", text="")
+ sub = row.row()
sub.active = field.use_min_distance
- sub.prop(field, "distance_min", text="Distance")
+ sub.prop(field, "distance_min", text="Minimum")
- sub = col.column()
+ col = split.column()
+ row = col.row(align=True)
+ row.prop(field, "use_max_distance", text="")
+ sub = row.row()
sub.active = field.use_max_distance
- sub.prop(field, "distance_max", text="Distance")
-
+ sub.prop(field, "distance_max", text="Maximum")
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py
index 8a697806aa1..9f96f0a5b9f 100644
--- a/release/scripts/ui/properties_physics_field.py
+++ b/release/scripts/startup/bl_ui/properties_physics_field.py
@@ -20,8 +20,10 @@
import bpy
-from properties_physics_common import basic_force_field_settings_ui
-from properties_physics_common import basic_force_field_falloff_ui
+from bl_ui.properties_physics_common import (
+ basic_force_field_settings_ui,
+ basic_force_field_falloff_ui,
+ )
class PhysicButtonsPanel():
@@ -55,7 +57,7 @@ class PHYSICS_PT_field(PhysicButtonsPanel, bpy.types.Panel):
split.prop(field, "type", text="")
- if field.type not in ('NONE', 'GUIDE', 'TEXTURE'):
+ if field.type not in {'NONE', 'GUIDE', 'TEXTURE'}:
split = layout.split(percentage=0.2)
split.label(text="Shape:")
split.prop(field, "shape", text="")
@@ -112,7 +114,7 @@ class PHYSICS_PT_field(PhysicButtonsPanel, bpy.types.Panel):
else:
basic_force_field_settings_ui(self, context, field)
- if field.type not in ('NONE', 'GUIDE'):
+ if field.type not in {'NONE', 'GUIDE'}:
layout.label(text="Falloff:")
layout.prop(field, "falloff_type", expand=True)
@@ -215,13 +217,5 @@ class PHYSICS_PT_collision(PhysicButtonsPanel, bpy.types.Panel):
col.label(text="Force Fields:")
col.prop(settings, "absorption", text="Absorption")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py
index 6385a535a44..5da89d0090a 100644
--- a/release/scripts/ui/properties_physics_fluid.py
+++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py
@@ -49,11 +49,11 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel, bpy.types.Panel):
return
row.prop(fluid, "type")
- if fluid.type not in ('NONE', 'DOMAIN', 'PARTICLE', 'FLUID'):
+ if fluid.type not in {'NONE', 'DOMAIN', 'PARTICLE', 'FLUID'}:
row.prop(fluid, "use", text="")
layout = layout.column()
- if fluid.type not in ('NONE', 'DOMAIN', 'PARTICLE', 'FLUID'):
+ if fluid.type not in {'NONE', 'DOMAIN', 'PARTICLE', 'FLUID'}:
layout.active = fluid.use
if fluid.type == 'DOMAIN':
@@ -123,7 +123,9 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel, bpy.types.Panel):
col.label(text="Volume Initialization:")
col.prop(fluid, "volume_initialization", text="")
col.prop(fluid, "use_animated_mesh")
- col.prop(fluid, "use_local_coords")
+ row = col.row()
+ row.active = not fluid.use_animated_mesh
+ row.prop(fluid, "use_local_coords")
col = split.column()
col.label(text="Inflow Velocity:")
@@ -280,13 +282,5 @@ class PHYSICS_PT_domain_particles(PhysicButtonsPanel, bpy.types.Panel):
col.prop(fluid, "tracer_particles")
col.prop(fluid, "generate_particles")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index 56ddff292d9..61d8d2e3825 100644
--- a/release/scripts/ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -20,8 +20,10 @@
import bpy
-from properties_physics_common import point_cache_ui
-from properties_physics_common import effector_weights_ui
+from bl_ui.properties_physics_common import (
+ point_cache_ui,
+ effector_weights_ui,
+ )
class PhysicButtonsPanel():
@@ -100,9 +102,6 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, bpy.types.Panel):
sub.prop(flow, "density")
sub.prop(flow, "temperature")
- #elif md.smoke_type == 'COLLISION':
- # layout.separator()
-
class PHYSICS_PT_smoke_groups(PhysicButtonsPanel, bpy.types.Panel):
bl_label = "Smoke Groups"
@@ -203,13 +202,5 @@ class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, bpy.types.Panel):
domain = context.smoke.domain_settings
effector_weights_ui(self, context, domain.effector_weights)
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_physics_softbody.py b/release/scripts/startup/bl_ui/properties_physics_softbody.py
index 7d3fa66ed02..61115a0590e 100644
--- a/release/scripts/ui/properties_physics_softbody.py
+++ b/release/scripts/startup/bl_ui/properties_physics_softbody.py
@@ -20,8 +20,10 @@
import bpy
-from properties_physics_common import point_cache_ui
-from properties_physics_common import effector_weights_ui
+from bl_ui.properties_physics_common import (
+ point_cache_ui,
+ effector_weights_ui,
+ )
def softbody_panel_enabled(md):
@@ -260,13 +262,5 @@ class PHYSICS_PT_softbody_field_weights(PhysicButtonsPanel, bpy.types.Panel):
effector_weights_ui(self, context, softbody.effector_weights)
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 5dfed9ccb20..a3b10702fa7 100644
--- a/release/scripts/ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -62,13 +62,9 @@ class RENDER_PT_render(RenderButtonsPanel, bpy.types.Panel):
rd = context.scene.render
- split = layout.split()
-
- col = split.column()
- col.operator("render.render", text="Image", icon='RENDER_STILL')
-
- col = split.column()
- col.operator("render.render", text="Animation", icon='RENDER_ANIMATION').animation = True
+ row = layout.row()
+ row.operator("render.render", text="Image", icon='RENDER_STILL')
+ row.operator("render.render", text="Animation", icon='RENDER_ANIMATION').animation = True
layout.prop(rd, "display_mode", text="Display")
@@ -293,6 +289,7 @@ class RENDER_PT_output(RenderButtonsPanel, bpy.types.Panel):
layout.prop(rd, "filepath", text="")
split = layout.split()
+
col = split.column()
col.prop(rd, "file_format", text="")
col.row().prop(rd, "color_mode", text="Color", expand=True)
@@ -302,36 +299,21 @@ class RENDER_PT_output(RenderButtonsPanel, bpy.types.Panel):
col.prop(rd, "use_overwrite")
col.prop(rd, "use_placeholder")
- if file_format in ('AVI_JPEG', 'JPEG'):
- split = layout.split()
- split.prop(rd, "file_quality", slider=True)
+ if file_format in {'AVI_JPEG', 'JPEG'}:
+ layout.prop(rd, "file_quality", slider=True)
if file_format == 'PNG':
- split = layout.split()
- split.prop(rd, "file_quality", slider=True, text="Compression")
-
- elif file_format == 'MULTILAYER':
- split = layout.split()
+ layout.prop(rd, "file_quality", slider=True, text="Compression")
- col = split.column()
- col.label(text="Codec:")
- col.prop(rd, "exr_codec", text="")
- col = split.column()
-
- elif file_format == 'OPEN_EXR':
- split = layout.split()
-
- col = split.column()
- col.label(text="Codec:")
- col.prop(rd, "exr_codec", text="")
+ if file_format in {'OPEN_EXR', 'MULTILAYER'}:
+ row = layout.row()
+ row.prop(rd, "exr_codec", text="Codec")
- subsplit = split.split()
- col = subsplit.column()
- col.prop(rd, "use_exr_half")
- col.prop(rd, "exr_zbuf")
-
- col = subsplit.column()
- col.prop(rd, "exr_preview")
+ if file_format == 'OPEN_EXR':
+ row = layout.row()
+ row.prop(rd, "use_exr_half")
+ row.prop(rd, "exr_zbuf")
+ row.prop(rd, "exr_preview")
elif file_format == 'JPEG2000':
split = layout.split()
@@ -343,7 +325,7 @@ class RENDER_PT_output(RenderButtonsPanel, bpy.types.Panel):
col.prop(rd, "jpeg2k_preset", text="")
col.prop(rd, "jpeg2k_ycc")
- elif file_format in ('CINEON', 'DPX'):
+ elif file_format in {'CINEON', 'DPX'}:
split = layout.split()
split.label("FIXME: hard coded Non-Linear, Gamma:1.0")
@@ -359,12 +341,10 @@ class RENDER_PT_output(RenderButtonsPanel, bpy.types.Panel):
'''
elif file_format == 'TIFF':
- split = layout.split()
- split.prop(rd, "use_tiff_16bit")
+ layout.prop(rd, "use_tiff_16bit")
elif file_format == 'QUICKTIME_CARBON':
- split = layout.split()
- split.operator("scene.render_data_set_quicktime_codec")
+ layout.operator("scene.render_data_set_quicktime_codec")
elif file_format == 'QUICKTIME_QTKIT':
split = layout.split()
@@ -376,12 +356,10 @@ class RENDER_PT_output(RenderButtonsPanel, bpy.types.Panel):
col.prop(rd, "quicktime_audiocodec_type", text="Audio Codec")
if rd.quicktime_audiocodec_type != 'No audio':
split = layout.split()
- col = split.column()
if rd.quicktime_audiocodec_type == 'LPCM':
- col.prop(rd, "quicktime_audio_bitdepth", text="")
+ split.prop(rd, "quicktime_audio_bitdepth", text="")
- col = split.column()
- col.prop(rd, "quicktime_audio_samplerate", text="")
+ split.prop(rd, "quicktime_audio_samplerate", text="")
split = layout.split()
col = split.column()
@@ -406,7 +384,7 @@ class RENDER_PT_encoding(RenderButtonsPanel, bpy.types.Panel):
@classmethod
def poll(cls, context):
rd = context.scene.render
- return rd.file_format in ('FFMPEG', 'XVID', 'H264', 'THEORA')
+ return rd.file_format in {'FFMPEG', 'XVID', 'H264', 'THEORA'}
def draw(self, context):
layout = self.layout
@@ -416,22 +394,15 @@ class RENDER_PT_encoding(RenderButtonsPanel, bpy.types.Panel):
layout.menu("RENDER_MT_ffmpeg_presets", text="Presets")
split = layout.split()
-
- col = split.column()
- col.prop(rd, "ffmpeg_format")
- if rd.ffmpeg_format in ('AVI', 'QUICKTIME', 'MKV', 'OGG'):
- col = split.column()
- col.prop(rd, "ffmpeg_codec")
+ split.prop(rd, "ffmpeg_format")
+ if rd.ffmpeg_format in {'AVI', 'QUICKTIME', 'MKV', 'OGG'}:
+ split.prop(rd, "ffmpeg_codec")
else:
split.label()
- split = layout.split()
-
- col = split.column()
- col.prop(rd, "ffmpeg_video_bitrate")
-
- col = split.column()
- col.prop(rd, "ffmpeg_gopsize")
+ row = layout.row()
+ row.prop(rd, "ffmpeg_video_bitrate")
+ row.prop(rd, "ffmpeg_gopsize")
split = layout.split()
@@ -442,28 +413,24 @@ class RENDER_PT_encoding(RenderButtonsPanel, bpy.types.Panel):
col.prop(rd, "ffmpeg_buffersize", text="Buffer")
col = split.column()
-
col.prop(rd, "ffmpeg_autosplit")
col.label(text="Mux:")
col.prop(rd, "ffmpeg_muxrate", text="Rate")
col.prop(rd, "ffmpeg_packetsize", text="Packet Size")
- # Audio:
- sub = layout.column()
-
- if rd.ffmpeg_format not in ('MP3', ):
- sub.prop(rd, "ffmpeg_audio_codec", text="Audio Codec")
+ layout.separator()
- sub.separator()
+ # Audio:
+ if rd.ffmpeg_format not in {'MP3'}:
+ layout.prop(rd, "ffmpeg_audio_codec", text="Audio Codec")
- split = sub.split()
+ split = layout.split()
col = split.column()
col.prop(rd, "ffmpeg_audio_bitrate")
col.prop(rd, "ffmpeg_audio_mixrate")
- col = split.column()
- col.prop(rd, "ffmpeg_audio_volume", slider=True)
+ split.prop(rd, "ffmpeg_audio_volume", slider=True)
class RENDER_PT_antialiasing(RenderButtonsPanel, bpy.types.Panel):
@@ -568,10 +535,7 @@ class RENDER_PT_dimensions(RenderButtonsPanel, bpy.types.Panel):
fps_rate = round(rd.fps / rd.fps_base, 2)
# TODO: Change the following to iterate over existing presets
- if (fps_rate in (23.98, 24, 25, 29.97, 30, 50, 59.94, 60)):
- custom_framerate = False
- else:
- custom_framerate = True
+ 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)"
@@ -580,7 +544,7 @@ class RENDER_PT_dimensions(RenderButtonsPanel, bpy.types.Panel):
sub.menu("RENDER_MT_framerate_presets", text=fps_label_text)
- if (bpy.types.RENDER_MT_framerate_presets.bl_label == "Custom") or (custom_framerate == True):
+ 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)
@@ -651,7 +615,7 @@ class RENDER_PT_bake(RenderButtonsPanel, bpy.types.Panel):
if rd.bake_type == 'NORMALS':
layout.prop(rd, "bake_normal_space")
- elif rd.bake_type in ('DISPLACEMENT', 'AO'):
+ elif rd.bake_type in {'DISPLACEMENT', 'AO'}:
layout.prop(rd, "use_bake_normalize")
# col.prop(rd, "bake_aa_mode")
@@ -673,13 +637,5 @@ class RENDER_PT_bake(RenderButtonsPanel, bpy.types.Panel):
sub.prop(rd, "bake_distance")
sub.prop(rd, "bake_bias")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py
index 51fe7c9e132..ce5ac0c62d3 100644
--- a/release/scripts/ui/properties_scene.py
+++ b/release/scripts/startup/bl_ui/properties_scene.py
@@ -55,14 +55,10 @@ class SCENE_PT_unit(SceneButtonsPanel, bpy.types.Panel):
col.row().prop(unit, "system", expand=True)
col.row().prop(unit, "system_rotation", expand=True)
- split = layout.split()
- split.active = (unit.system != 'NONE')
-
- col = split.column()
- col.prop(unit, "scale_length", text="Scale")
-
- col = split.column()
- col.prop(unit, "use_separate")
+ row = layout.row()
+ row.active = (unit.system != 'NONE')
+ row.prop(unit, "scale_length", text="Scale")
+ row.prop(unit, "use_separate")
class SCENE_PT_keying_sets(SceneButtonsPanel, bpy.types.Panel):
@@ -199,9 +195,6 @@ class SCENE_PT_custom_props(SceneButtonsPanel, PropertyPanel, bpy.types.Panel):
_context_path = "scene"
_property_type = bpy.types.Scene
-
-from bpy.props import *
-
# XXX, move operator to op/ dir
@@ -310,83 +303,75 @@ class ANIM_OT_keying_set_export(bpy.types.Operator):
return {'RUNNING_MODAL'}
class SCENE_PT_navmesh(SceneButtonsPanel, bpy.types.Panel):
- bl_label = "Navmesh"
- bl_default_closed = True
- COMPAT_ENGINES = {'BLENDER_GAME'}
-
- def draw(self, context):
- layout = self.layout
-
- rd = context.scene.game_settings.recast_data
-
- layout.operator("object.create_navmesh", text='Build navigation mesh')
-
- layout.label(text="Rasterization:")
- split = layout.split()
-
- col = split.column()
- col.prop(rd, "cell_size")
- col = split.column()
- col.prop(rd, "cell_height")
-
- layout.separator()
-
- layout.label(text="Agent:")
- split = layout.split()
-
- col = split.column()
- row = col.row()
- row.prop(rd, "agent_height")
- row = col.row()
- row.prop(rd, "agent_radius")
-
- col = split.column()
- row = col.row()
- row.prop(rd, "max_slope")
- row = col.row()
- row.prop(rd, "max_climb")
-
- layout.separator()
-
- layout.label(text="Region:")
- split = layout.split()
- col = split.column()
- col.prop(rd, "region_min_size")
-
- col = split.column()
- col.prop(rd, "region_merge_size")
-
- layout.separator()
-
- layout.label(text="Polygonization:")
- split = layout.split()
- col = split.column()
- row = col.row()
- row.prop(rd, "edge_max_len")
- row = col.row()
- row.prop(rd, "edge_max_error")
-
- col = split.column()
- row = col.row()
- row.prop(rd, "verts_per_poly")
-
- layout.separator()
-
- layout.label(text="Detail Mesh:")
- split = layout.split()
- col = split.column()
- col.prop(rd, "sample_dist")
-
- col = split.column()
- col.prop(rd, "sample_max_error")
-
-
-def register():
+ bl_label = "Navmesh"
+ bl_default_closed = True
+ COMPAT_ENGINES = {'BLENDER_GAME'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ rd = context.scene.game_settings.recast_data
+
+ layout.operator("object.create_navmesh", text='Build navigation mesh')
+
+ layout.label(text="Rasterization:")
+ split = layout.split()
+
+ col = split.column()
+ col.prop(rd, "cell_size")
+ col = split.column()
+ col.prop(rd, "cell_height")
+
+ layout.separator()
+
+ layout.label(text="Agent:")
+ split = layout.split()
+
+ col = split.column()
+ row = col.row()
+ row.prop(rd, "agent_height")
+ row = col.row()
+ row.prop(rd, "agent_radius")
+
+ col = split.column()
+ row = col.row()
+ row.prop(rd, "max_slope")
+ row = col.row()
+ row.prop(rd, "max_climb")
+
+ layout.separator()
+
+ layout.label(text="Region:")
+ split = layout.split()
+ col = split.column()
+ col.prop(rd, "region_min_size")
+
+ col = split.column()
+ col.prop(rd, "region_merge_size")
+
+ layout.separator()
+
+ layout.label(text="Polygonization:")
+ split = layout.split()
+ col = split.column()
+ row = col.row()
+ row.prop(rd, "edge_max_len")
+ row = col.row()
+ row.prop(rd, "edge_max_error")
+
+ col = split.column()
+ row = col.row()
+ row.prop(rd, "verts_per_poly")
+
+ layout.separator()
+
+ layout.label(text="Detail Mesh:")
+ split = layout.split()
+ col = split.column()
+ col.prop(rd, "sample_dist")
+
+ col = split.column()
+ col.prop(rd, "sample_max_error")
+
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py
index 05e130fac2d..01890bc3c99 100644
--- a/release/scripts/ui/properties_texture.py
+++ b/release/scripts/startup/bl_ui/properties_texture.py
@@ -43,7 +43,7 @@ class TEXTURE_MT_envmap_specials(bpy.types.Menu):
layout.operator("texture.envmap_clear", icon='FILE_REFRESH')
layout.operator("texture.envmap_clear_all", icon='FILE_REFRESH')
-from properties_material import active_node_mat
+from bl_ui.properties_material import active_node_mat
def context_tex_datablock(context):
@@ -60,6 +60,12 @@ def context_tex_datablock(context):
return idblock
idblock = context.brush
+ if idblock:
+ return idblock
+
+ if context.particle_system:
+ idblock = context.particle_system.settings
+
return idblock
@@ -84,7 +90,7 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, bpy.types.Panel):
engine = context.scene.render.engine
if not hasattr(context, "texture_slot"):
return False
- return ((context.material or context.world or context.lamp or context.brush or context.texture)
+ 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):
@@ -96,13 +102,14 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, bpy.types.Panel):
idblock = context_tex_datablock(context)
pin_id = space.pin_id
- if not isinstance(pin_id, bpy.types.Material):
+ if space.use_pin_id and not isinstance(pin_id, bpy.types.Texture):
+ idblock = pin_id
pin_id = None
if not space.use_pin_id:
layout.prop(space, "texture_context", expand=True)
- tex_collection = (not space.use_pin_id) and (node is None) and (not isinstance(idblock, bpy.types.Brush))
+ tex_collection = (pin_id is None) and (node is None) and (not isinstance(idblock, bpy.types.Brush))
if tex_collection:
row = layout.row()
@@ -237,8 +244,7 @@ class TEXTURE_PT_clouds(TextureTypePanel, bpy.types.Panel):
col.prop(tex, "noise_scale", text="Size")
col.prop(tex, "noise_depth", text="Depth")
- col = split.column()
- col.prop(tex, "nabla", text="Nabla")
+ split.prop(tex, "nabla", text="Nabla")
class TEXTURE_PT_wood(TextureTypePanel, bpy.types.Panel):
@@ -251,24 +257,23 @@ class TEXTURE_PT_wood(TextureTypePanel, bpy.types.Panel):
tex = context.texture
- layout.prop(tex, "noisebasis_2", expand=True)
+ layout.prop(tex, "noise_basis_2", expand=True)
layout.prop(tex, "wood_type", expand=True)
col = layout.column()
- col.active = tex.wood_type in ('RINGNOISE', 'BANDNOISE')
+ col.active = tex.wood_type in {'RINGNOISE', 'BANDNOISE'}
col.label(text="Noise:")
col.row().prop(tex, "noise_type", text="Type", expand=True)
layout.prop(tex, "noise_basis", text="Basis")
split = layout.split()
- split.active = tex.wood_type in ('RINGNOISE', 'BANDNOISE')
+ split.active = tex.wood_type in {'RINGNOISE', 'BANDNOISE'}
col = split.column()
col.prop(tex, "noise_scale", text="Size")
col.prop(tex, "turbulence")
- col = split.column()
- col.prop(tex, "nabla")
+ split.prop(tex, "nabla")
class TEXTURE_PT_marble(TextureTypePanel, bpy.types.Panel):
@@ -282,7 +287,7 @@ class TEXTURE_PT_marble(TextureTypePanel, bpy.types.Panel):
tex = context.texture
layout.prop(tex, "marble_type", expand=True)
- layout.prop(tex, "noisebasis_2", expand=True)
+ layout.prop(tex, "noise_basis_2", expand=True)
layout.label(text="Noise:")
layout.prop(tex, "noise_type", text="Type", expand=True)
layout.prop(tex, "noise_basis", text="Basis")
@@ -308,13 +313,9 @@ class TEXTURE_PT_magic(TextureTypePanel, bpy.types.Panel):
tex = context.texture
- split = layout.split()
-
- col = split.column()
- col.prop(tex, "noise_depth", text="Depth")
-
- col = split.column()
- col.prop(tex, "turbulence")
+ row = layout.row()
+ row.prop(tex, "noise_depth", text="Depth")
+ row.prop(tex, "turbulence")
class TEXTURE_PT_blend(TextureTypePanel, bpy.types.Panel):
@@ -331,7 +332,7 @@ class TEXTURE_PT_blend(TextureTypePanel, bpy.types.Panel):
sub = layout.row()
- sub.active = (tex.progression in ('LINEAR', 'QUADRATIC', 'EASING', 'RADIAL'))
+ sub.active = (tex.progression in {'LINEAR', 'QUADRATIC', 'EASING', 'RADIAL'})
sub.prop(tex, "use_flip_axis", expand=True)
@@ -350,13 +351,9 @@ class TEXTURE_PT_stucci(TextureTypePanel, bpy.types.Panel):
layout.prop(tex, "noise_type", text="Type", expand=True)
layout.prop(tex, "noise_basis", text="Basis")
- split = layout.split()
-
- col = split.column()
- col.prop(tex, "noise_scale", text="Size")
-
- col = split.column()
- col.prop(tex, "turbulence")
+ row = layout.row()
+ row.prop(tex, "noise_scale", text="Size")
+ row.prop(tex, "turbulence")
class TEXTURE_PT_image(TextureTypePanel, bpy.types.Panel):
@@ -375,7 +372,7 @@ class TEXTURE_PT_image(TextureTypePanel, bpy.types.Panel):
def texture_filter_common(tex, layout):
layout.label(text="Filter:")
layout.prop(tex, "filter_type", text="")
- if tex.use_mipmap and tex.filter_type in ('AREA', 'EWA', 'FELINE'):
+ if tex.use_mipmap and tex.filter_type in {'AREA', 'EWA', 'FELINE'}:
if tex.filter_type == 'FELINE':
layout.prop(tex, "filter_probes", text="Probes")
else:
@@ -555,24 +552,20 @@ class TEXTURE_PT_musgrave(TextureTypePanel, bpy.types.Panel):
musgrave_type = tex.musgrave_type
col = split.column()
- if musgrave_type in ('HETERO_TERRAIN', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'):
+ if musgrave_type in {'HETERO_TERRAIN', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}:
col.prop(tex, "offset")
- if musgrave_type in ('MULTIFRACTAL', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'):
+ if musgrave_type in {'MULTIFRACTAL', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}:
col.prop(tex, "noise_intensity", text="Intensity")
- if musgrave_type in ('RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'):
+ if musgrave_type in {'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}:
col.prop(tex, "gain")
layout.label(text="Noise:")
layout.prop(tex, "noise_basis", text="Basis")
- split = layout.split()
-
- col = split.column()
- col.prop(tex, "noise_scale", text="Size")
-
- col = split.column()
- col.prop(tex, "nabla")
+ row = layout.row()
+ row.prop(tex, "noise_scale", text="Size")
+ row.prop(tex, "nabla")
class TEXTURE_PT_voronoi(TextureTypePanel, bpy.types.Panel):
@@ -606,14 +599,9 @@ class TEXTURE_PT_voronoi(TextureTypePanel, bpy.types.Panel):
sub.prop(tex, "weight_4", text="4", slider=True)
layout.label(text="Noise:")
-
- split = layout.split()
-
- col = split.column()
- col.prop(tex, "noise_scale", text="Size")
-
- col = split.column()
- col.prop(tex, "nabla")
+ row = layout.row()
+ row.prop(tex, "noise_scale", text="Size")
+ row.prop(tex, "nabla")
class TEXTURE_PT_distortednoise(TextureTypePanel, bpy.types.Panel):
@@ -635,8 +623,7 @@ class TEXTURE_PT_distortednoise(TextureTypePanel, bpy.types.Panel):
col.prop(tex, "distortion", text="Distortion")
col.prop(tex, "noise_scale", text="Size")
- col = split.column()
- col.prop(tex, "nabla")
+ split.prop(tex, "nabla")
class TEXTURE_PT_voxeldata(TextureButtonsPanel, bpy.types.Panel):
@@ -656,7 +643,7 @@ class TEXTURE_PT_voxeldata(TextureButtonsPanel, bpy.types.Panel):
vd = tex.voxel_data
layout.prop(vd, "file_format")
- if vd.file_format in ('BLENDER_VOXEL', 'RAW_8BIT'):
+ if vd.file_format in {'BLENDER_VOXEL', 'RAW_8BIT'}:
layout.prop(vd, "filepath")
if vd.file_format == 'RAW_8BIT':
layout.prop(vd, "resolution")
@@ -668,7 +655,7 @@ class TEXTURE_PT_voxeldata(TextureButtonsPanel, bpy.types.Panel):
layout.template_image(tex, "image", tex.image_user, compact=True)
#layout.prop(vd, "frame_duration")
- if vd.file_format in ('BLENDER_VOXEL', 'RAW_8BIT'):
+ if vd.file_format in {'BLENDER_VOXEL', 'RAW_8BIT'}:
layout.prop(vd, "use_still_frame")
row = layout.row()
row.active = vd.use_still_frame
@@ -719,12 +706,13 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, bpy.types.Panel):
col.separator()
- col.label(text="Color Source:")
- col.prop(pd, "color_source", text="")
- if pd.color_source in ('PARTICLE_SPEED', 'PARTICLE_VELOCITY'):
- col.prop(pd, "speed_scale")
- if pd.color_source in ('PARTICLE_SPEED', 'PARTICLE_AGE'):
- layout.template_color_ramp(pd, "color_ramp", expand=True)
+ if pd.point_source == 'PARTICLE_SYSTEM':
+ col.label(text="Color Source:")
+ col.prop(pd, "color_source", text="")
+ if pd.color_source in {'PARTICLE_SPEED', 'PARTICLE_VELOCITY'}:
+ col.prop(pd, "speed_scale")
+ if pd.color_source in {'PARTICLE_SPEED', 'PARTICLE_AGE'}:
+ layout.template_color_ramp(pd, "color_ramp", expand=True)
col = split.column()
col.label()
@@ -733,6 +721,15 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel, bpy.types.Panel):
col.prop(pd, "falloff", text="")
if pd.falloff == 'SOFT':
col.prop(pd, "falloff_soft")
+ if pd.falloff == "PARTICLE_VELOCITY":
+ col.prop(pd, "falloff_speed_scale")
+
+ col.prop(pd, "use_falloff_curve")
+
+ if pd.use_falloff_curve:
+ col = layout.column()
+ col.label(text="Falloff Curve")
+ col.template_curve_mapping(pd, "falloff_curve", brush=False)
class TEXTURE_PT_pointdensity_turbulence(TextureButtonsPanel, bpy.types.Panel):
@@ -746,12 +743,9 @@ class TEXTURE_PT_pointdensity_turbulence(TextureButtonsPanel, bpy.types.Panel):
return tex and (tex.type == 'POINT_DENSITY' and (engine in cls.COMPAT_ENGINES))
def draw_header(self, context):
- layout = self.layout
+ pd = context.texture.point_density
- tex = context.texture
- pd = tex.point_density
-
- layout.prop(pd, "use_turbulence", text="")
+ self.layout.prop(pd, "use_turbulence", text="")
def draw(self, context):
layout = self.layout
@@ -834,7 +828,7 @@ class TEXTURE_PT_mapping(TextureSlotPanel, bpy.types.Panel):
layout.prop(tex, "map_mode", expand=True)
row = layout.row()
- row.active = tex.map_mode in ('FIXED', 'TILED')
+ row.active = tex.map_mode in {'FIXED', 'TILED'}
row.prop(tex, "angle")
else:
if isinstance(idblock, bpy.types.Material):
@@ -845,7 +839,7 @@ class TEXTURE_PT_mapping(TextureSlotPanel, bpy.types.Panel):
split = layout.split()
col = split.column()
- if tex.texture_coords in ('ORCO', 'UV'):
+ if tex.texture_coords in {'ORCO', 'UV'}:
col.prop(tex, "use_from_dupli")
elif tex.texture_coords == 'OBJECT':
col.prop(tex, "use_from_original")
@@ -858,14 +852,9 @@ class TEXTURE_PT_mapping(TextureSlotPanel, bpy.types.Panel):
row.prop(tex, "mapping_y", text="")
row.prop(tex, "mapping_z", text="")
- split = layout.split()
-
- col = split.column()
- col.prop(tex, "offset")
-
- col = split.column()
-
- col.prop(tex, "scale")
+ row = layout.row()
+ row.column().prop(tex, "offset")
+ row.column().prop(tex, "scale")
class TEXTURE_PT_influence(TextureSlotPanel, bpy.types.Panel):
@@ -902,7 +891,7 @@ class TEXTURE_PT_influence(TextureSlotPanel, bpy.types.Panel):
return sub # XXX, temp. use_map_normal needs to override.
if isinstance(idblock, bpy.types.Material):
- if idblock.type in ('SURFACE', 'WIRE'):
+ if idblock.type in {'SURFACE', 'WIRE'}:
split = layout.split()
col = split.column()
@@ -983,38 +972,65 @@ class TEXTURE_PT_influence(TextureSlotPanel, bpy.types.Panel):
col = split.column()
factor_but(col, "use_map_zenith_up", "zenith_up_factor", "Zenith Up")
factor_but(col, "use_map_zenith_down", "zenith_down_factor", "Zenith Down")
+ elif isinstance(idblock, bpy.types.ParticleSettings):
+ split = layout.split()
+
+ col = split.column()
+ col.label(text="General:")
+ factor_but(col, "use_map_time", "time_factor", "Time")
+ factor_but(col, "use_map_life", "life_factor", "Lifetime")
+ factor_but(col, "use_map_density", "density_factor", "Density")
+ factor_but(col, "use_map_size", "size_factor", "Size")
+
+ col = split.column()
+ col.label(text="Physics:")
+ factor_but(col, "use_map_velocity", "velocity_factor", "Velocity")
+ factor_but(col, "use_map_damp", "damp_factor", "Damp")
+ factor_but(col, "use_map_gravity", "gravity_factor", "Gravity")
+ factor_but(col, "use_map_field", "field_factor", "Force Fields")
+
+ layout.label(text="Hair:")
+
+ split = layout.split()
+
+ col = split.column()
+ factor_but(col, "use_map_length", "length_factor", "Length")
+ factor_but(col, "use_map_clump", "clump_factor", "Clump")
+
+ col = split.column()
+ factor_but(col, "use_map_kink", "kink_factor", "Kink")
+ factor_but(col, "use_map_rough", "rough_factor", "Rough")
layout.separator()
- split = layout.split()
+ if not isinstance(idblock, bpy.types.ParticleSettings):
+ split = layout.split()
- col = split.column()
- col.prop(tex, "blend_type", text="Blend")
- col.prop(tex, "use_rgb_to_intensity")
- # color is used on grayscale textures even when use_rgb_to_intensity is disabled.
- col.prop(tex, "color", text="")
+ col = split.column()
+ col.prop(tex, "blend_type", text="Blend")
+ col.prop(tex, "use_rgb_to_intensity")
+ # color is used on grayscale textures even when use_rgb_to_intensity is disabled.
+ col.prop(tex, "color", text="")
- col = split.column()
- col.prop(tex, "invert", text="Negative")
- col.prop(tex, "use_stencil")
+ col = split.column()
+ col.prop(tex, "invert", text="Negative")
+ col.prop(tex, "use_stencil")
if isinstance(idblock, bpy.types.Material) or isinstance(idblock, bpy.types.World):
col.prop(tex, "default_value", text="DVar", slider=True)
if isinstance(idblock, bpy.types.Material):
- row = layout.row()
- row.label(text="Bump Mapping:")
+ layout.label(text="Bump Mapping:")
- row = layout.row()
# only show bump settings if activated but not for normalmap images
- row.active = tex.use_map_normal and not (tex.texture.type == 'IMAGE' and tex.texture.use_normal_map)
+ row = layout.row()
+ row.active = (tex.use_map_normal or tex.use_map_warp) and not (tex.texture.type == 'IMAGE' and tex.texture.use_normal_map)
- col = row.column()
- col.prop(tex, "bump_method", text="Method")
+ row.prop(tex, "bump_method", text="Method")
- col = row.column()
- col.prop(tex, "bump_objectspace", text="Space")
- col.active = tex.bump_method in ('BUMP_DEFAULT', 'BUMP_BEST_QUALITY')
+ sub = row.row()
+ sub.active = tex.bump_method in {'BUMP_DEFAULT', 'BUMP_BEST_QUALITY'}
+ sub.prop(tex, "bump_objectspace", text="Space")
class TEXTURE_PT_custom_props(TextureButtonsPanel, PropertyPanel, bpy.types.Panel):
@@ -1022,13 +1038,5 @@ class TEXTURE_PT_custom_props(TextureButtonsPanel, PropertyPanel, bpy.types.Pane
_context_path = "texture"
_property_type = bpy.types.Texture
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/properties_world.py b/release/scripts/startup/bl_ui/properties_world.py
index fd11f299f34..4f398c9fbd9 100644
--- a/release/scripts/ui/properties_world.py
+++ b/release/scripts/startup/bl_ui/properties_world.py
@@ -266,13 +266,5 @@ class WORLD_PT_custom_props(WorldButtonsPanel, PropertyPanel, bpy.types.Panel):
_context_path = "world"
_property_type = bpy.types.World
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_console.py b/release/scripts/startup/bl_ui/space_console.py
index b3bd886d8c5..da6c102100b 100644
--- a/release/scripts/ui/space_console.py
+++ b/release/scripts/startup/bl_ui/space_console.py
@@ -18,15 +18,13 @@
# <pep8 compliant>
import bpy
-from bpy.props import *
+from bpy.props import StringProperty
class CONSOLE_HT_header(bpy.types.Header):
bl_space_type = 'CONSOLE'
def draw(self, context):
- sc = context.space_data
- # text = sc.text
layout = self.layout
row = layout.row(align=True)
@@ -161,13 +159,5 @@ class ConsoleLanguage(bpy.types.Operator):
return {'FINISHED'}
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 9a1acfd76b8..930a2029d32 100644
--- a/release/scripts/ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -33,48 +33,52 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
row.prop(dopesheet, "show_only_selected", text="")
row.prop(dopesheet, "show_hidden", text="")
- if genericFiltersOnly:
- return
-
- row = layout.row(align=True)
- row.prop(dopesheet, "show_transforms", text="")
+ if not genericFiltersOnly:
+ row = layout.row(align=True)
+ row.prop(dopesheet, "show_transforms", text="")
- if is_nla:
- row.prop(dopesheet, "show_missing_nla", text="")
+ if is_nla:
+ row.prop(dopesheet, "show_missing_nla", text="")
- row = layout.row(align=True)
- row.prop(dopesheet, "show_scenes", text="")
- row.prop(dopesheet, "show_worlds", text="")
- row.prop(dopesheet, "show_nodes", text="")
-
- if bpy.data.meshes:
- row.prop(dopesheet, "show_meshes", text="")
- if bpy.data.shape_keys:
- row.prop(dopesheet, "show_shapekeys", text="")
- if bpy.data.materials:
- row.prop(dopesheet, "show_materials", text="")
- if bpy.data.lamps:
- row.prop(dopesheet, "show_lamps", text="")
- if bpy.data.textures:
- row.prop(dopesheet, "show_textures", text="")
- if bpy.data.cameras:
- row.prop(dopesheet, "show_cameras", text="")
- if bpy.data.curves:
- row.prop(dopesheet, "show_curves", text="")
- if bpy.data.metaballs:
- row.prop(dopesheet, "show_metaballs", text="")
- if bpy.data.lattices:
- row.prop(dopesheet, "show_lattices", text="")
- if bpy.data.armatures:
- row.prop(dopesheet, "show_armatures", text="")
- if bpy.data.particles:
- row.prop(dopesheet, "show_particles", text="")
-
- if bpy.data.groups:
row = layout.row(align=True)
- row.prop(dopesheet, "show_only_group_objects", text="")
- if dopesheet.show_only_group_objects:
- row.prop(dopesheet, "filter_group", text="")
+ row.prop(dopesheet, "show_scenes", text="")
+ row.prop(dopesheet, "show_worlds", text="")
+ row.prop(dopesheet, "show_nodes", text="")
+
+ if bpy.data.meshes:
+ row.prop(dopesheet, "show_meshes", text="")
+ if bpy.data.shape_keys:
+ row.prop(dopesheet, "show_shapekeys", text="")
+ if bpy.data.materials:
+ row.prop(dopesheet, "show_materials", text="")
+ if bpy.data.lamps:
+ row.prop(dopesheet, "show_lamps", text="")
+ if bpy.data.textures:
+ row.prop(dopesheet, "show_textures", text="")
+ if bpy.data.cameras:
+ row.prop(dopesheet, "show_cameras", text="")
+ if bpy.data.curves:
+ row.prop(dopesheet, "show_curves", text="")
+ if bpy.data.metaballs:
+ row.prop(dopesheet, "show_metaballs", text="")
+ if bpy.data.lattices:
+ row.prop(dopesheet, "show_lattices", text="")
+ if bpy.data.armatures:
+ row.prop(dopesheet, "show_armatures", text="")
+ if bpy.data.particles:
+ row.prop(dopesheet, "show_particles", text="")
+
+ if bpy.data.groups:
+ row = layout.row(align=True)
+ row.prop(dopesheet, "show_only_group_objects", text="")
+ if dopesheet.show_only_group_objects:
+ row.prop(dopesheet, "filter_group", text="")
+
+ if not is_nla:
+ row = layout.row(align=True)
+ row.prop(dopesheet, "show_only_matching_fcurves", text="")
+ if dopesheet.show_only_matching_fcurves:
+ row.prop(dopesheet, "filter_fcurve_name", text="")
#######################################
@@ -118,7 +122,7 @@ class DOPESHEET_HT_header(bpy.types.Header):
# filters which will work here and are useful (especially for character animation)
dopesheet_filter(layout, context, genericFiltersOnly=True)
- if st.mode in ('ACTION', 'SHAPEKEY'):
+ if st.mode in {'ACTION', 'SHAPEKEY'}:
layout.template_ID(st, "action", new="action.new")
# Grease Pencil mode doesn't need snapping, as it's frame-aligned only
@@ -159,6 +163,7 @@ class DOPESHEET_MT_view(bpy.types.Menu):
layout.separator()
layout.operator("action.frame_jump")
layout.operator("action.view_all")
+ layout.operator("action.view_selected")
layout.separator()
layout.operator("screen.area_dupli")
@@ -187,6 +192,10 @@ class DOPESHEET_MT_select(bpy.types.Menu):
layout.operator("action.select_column", text="Columns on Selected Markers").mode = 'MARKERS_COLUMN'
layout.operator("action.select_column", text="Between Selected Markers").mode = 'MARKERS_BETWEEN'
+ layout.separator()
+ layout.operator("action.select_leftright", text="Before Current Frame").mode = 'LEFT'
+ layout.operator("action.select_leftright", text="After Current Frame").mode = 'RIGHT'
+
# FIXME: grease pencil mode isn't supported for these yet, so skip for that mode only
if context.space_data.mode != 'GPENCIL':
layout.separator()
@@ -217,10 +226,13 @@ class DOPESHEET_MT_marker(bpy.types.Menu):
layout.operator("marker.rename", text="Rename Marker")
layout.operator("marker.move", text="Grab/Move Marker")
- if st.mode in ('ACTION', 'SHAPEKEY') and st.action:
+ if st.mode in {'ACTION', 'SHAPEKEY'} and st.action:
layout.separator()
layout.prop(st, "show_pose_markers")
+ if st.show_pose_markers is False:
+ layout.operator("action.markers_make_local")
+
#######################################
# Keyframe Editing
@@ -353,13 +365,5 @@ class DOPESHEET_MT_gpencil_frame(bpy.types.Menu):
#layout.operator("action.copy")
#layout.operator("action.paste")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 2d66bb38637..73fe1a97252 100644
--- a/release/scripts/ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -43,7 +43,7 @@ class FILEBROWSER_HT_header(bpy.types.Header):
row.separator()
row = layout.row(align=True)
- row.operator("file.directory_new", text="", icon='NEWFOLDER')
+ row.operator("file.directory_new", icon='NEWFOLDER')
params = st.params
@@ -73,13 +73,5 @@ class FILEBROWSER_HT_header(bpy.types.Header):
row.prop(params, "use_filter_sound", text="")
row.prop(params, "use_filter_text", text="")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index 55c4d4cfd94..bfc1a0e3a23 100644
--- a/release/scripts/ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -25,7 +25,7 @@ class GRAPH_HT_header(bpy.types.Header):
bl_space_type = 'GRAPH_EDITOR'
def draw(self, context):
- from space_dopesheet import dopesheet_filter
+ from bl_ui.space_dopesheet import dopesheet_filter
layout = self.layout
@@ -100,6 +100,7 @@ class GRAPH_MT_view(bpy.types.Menu):
layout.separator()
layout.operator("graph.frame_jump")
layout.operator("graph.view_all")
+ layout.operator("graph.view_selected")
layout.separator()
layout.operator("screen.area_dupli")
@@ -130,6 +131,10 @@ class GRAPH_MT_select(bpy.types.Menu):
layout.operator("graph.select_column", text="Between Selected Markers").mode = 'MARKERS_BETWEEN'
layout.separator()
+ layout.operator("graph.select_leftright", text="Before Current Frame").mode = 'LEFT'
+ layout.operator("graph.select_leftright", text="After Current Frame").mode = 'RIGHT'
+
+ layout.separator()
layout.operator("graph.select_more")
layout.operator("graph.select_less")
@@ -205,6 +210,7 @@ class GRAPH_MT_key(bpy.types.Menu):
layout.separator()
layout.operator("graph.keyframe_insert")
layout.operator("graph.fmodifier_add")
+ layout.operator("graph.sound_bake")
layout.separator()
layout.operator("graph.duplicate")
@@ -240,13 +246,5 @@ class GRAPH_MT_key_transform(bpy.types.Menu):
layout.operator("transform.rotate", text="Rotate")
layout.operator("transform.resize", text="Scale")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index 433c12b0620..77583b80824 100644
--- a/release/scripts/ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -91,8 +91,8 @@ class IMAGE_MT_select(bpy.types.Menu):
layout.separator()
layout.operator("uv.select_all")
- layout.operator("uv.select_inverse")
- layout.operator("uv.unlink_selection")
+ layout.operator("uv.select_all", text="Inverse").action = 'INVERT'
+ layout.operator("uv.unlink_selected")
layout.separator()
@@ -128,6 +128,10 @@ class IMAGE_MT_image(bpy.types.Menu):
layout.operator("image.external_edit", "Edit Externally")
+ layout.separator()
+
+ layout.menu("IMAGE_MT_image_invert")
+
if not show_render:
layout.separator()
@@ -139,7 +143,7 @@ class IMAGE_MT_image(bpy.types.Menu):
# only for dirty && specific image types, perhaps
# this could be done in operator poll too
if ima.is_dirty:
- if ima.source in ('FILE', 'GENERATED') and ima.type != 'MULTILAYER':
+ if ima.source in {'FILE', 'GENERATED'} and ima.type != 'MULTILAYER':
layout.operator("image.pack", text="Pack As PNG").as_png = True
layout.separator()
@@ -147,6 +151,32 @@ class IMAGE_MT_image(bpy.types.Menu):
layout.prop(sima, "use_image_paint")
+class IMAGE_MT_image_invert(bpy.types.Menu):
+ bl_label = "Invert"
+
+ def draw(self, context):
+ layout = self.layout
+
+ op = layout.operator("image.invert", text="Invert Image Colors")
+ op.invert_r = True
+ op.invert_g = True
+ op.invert_b = True
+
+ layout.separator()
+
+ op = layout.operator("image.invert", text="Invert Red Channel")
+ op.invert_r = True
+
+ op = layout.operator("image.invert", text="Invert Green Channel")
+ op.invert_g = True
+
+ op = layout.operator("image.invert", text="Invert Blue Channel")
+ op.invert_b = True
+
+ op = layout.operator("image.invert", text="Invert Alpha Channel")
+ op.invert_a = True
+
+
class IMAGE_MT_uvs_showhide(bpy.types.Menu):
bl_label = "Show/Hide Faces"
@@ -176,14 +206,14 @@ class IMAGE_MT_uvs_snap(bpy.types.Menu):
layout = self.layout
layout.operator_context = 'EXEC_REGION_WIN'
- layout.operator("uv.snap_selection", text="Selected to Pixels").target = 'PIXELS'
- layout.operator("uv.snap_selection", text="Selected to Cursor").target = 'CURSOR'
- layout.operator("uv.snap_selection", text="Selected to Adjacent Unselected").target = 'ADJACENT_UNSELECTED'
+ layout.operator("uv.snap_selected", text="Selected to Pixels").target = 'PIXELS'
+ layout.operator("uv.snap_selected", text="Selected to Cursor").target = 'CURSOR'
+ layout.operator("uv.snap_selected", text="Selected to Adjacent Unselected").target = 'ADJACENT_UNSELECTED'
layout.separator()
layout.operator("uv.snap_cursor", text="Cursor to Pixels").target = 'PIXELS'
- layout.operator("uv.snap_cursor", text="Cursor to Selection").target = 'SELECTION'
+ layout.operator("uv.snap_cursor", text="Cursor to Selected").target = 'SELECTED'
class IMAGE_MT_uvs_mirror(bpy.types.Menu):
@@ -204,7 +234,7 @@ class IMAGE_MT_uvs_weldalign(bpy.types.Menu):
layout = self.layout
layout.operator("uv.weld") # W, 1
- layout.operator_enums("uv.align", "axis") # W, 2/3/4
+ layout.operator_enum("uv.align", "axis") # W, 2/3/4
class IMAGE_MT_uvs(bpy.types.Menu):
@@ -233,7 +263,7 @@ class IMAGE_MT_uvs(bpy.types.Menu):
layout.operator("uv.average_islands_scale")
layout.operator("uv.minimize_stretch")
layout.operator("uv.stitch")
- layout.operator("mesh.faces_miror_uv")
+ layout.operator("mesh.faces_mirror_uv")
layout.separator()
@@ -340,10 +370,7 @@ class IMAGE_HT_header(bpy.types.Header):
layout.prop(toolsettings, "use_uv_select_sync", text="")
if toolsettings.use_uv_select_sync:
- row = layout.row(align=True)
- row.prop(toolsettings, "mesh_select_mode", text="", index=0, icon='VERTEXSEL')
- row.prop(toolsettings, "mesh_select_mode", text="", index=1, icon='EDGESEL')
- row.prop(toolsettings, "mesh_select_mode", text="", index=2, icon='FACESEL')
+ layout.template_edit_mode_selection()
else:
layout.prop(toolsettings, "uv_select_mode", text="", expand=True)
layout.prop(uvedit, "sticky_select_mode", text="", icon_only=True)
@@ -357,8 +384,8 @@ class IMAGE_HT_header(bpy.types.Header):
row.prop(toolsettings, "use_snap", text="")
row.prop(toolsettings, "snap_element", text="", icon_only=True)
- # mesh = context.edit_object.data
- # row.prop_search(mesh.uv_textures, "active", mesh, "uv_textures")
+ mesh = context.edit_object.data
+ layout.prop_search(mesh.uv_textures, "active", mesh, "uv_textures", text="")
if ima:
# layers
@@ -374,7 +401,7 @@ class IMAGE_HT_header(bpy.types.Header):
row = layout.row(align=True)
if ima.type == 'COMPOSITE':
row.operator("image.record_composite", icon='REC')
- if ima.type == 'COMPOSITE' and ima.source in ('MOVIE', 'SEQUENCE'):
+ if ima.type == 'COMPOSITE' and ima.source in {'MOVIE', 'SEQUENCE'}:
row.operator("image.play_composite", icon='PLAY')
if show_uvedit or sima.use_image_paint:
@@ -395,7 +422,6 @@ class IMAGE_PT_image_properties(bpy.types.Panel):
layout = self.layout
sima = context.space_data
- # ima = sima.image
iuser = sima.image_user
layout.template_image(sima, "image", iuser)
@@ -578,7 +604,9 @@ class IMAGE_PT_view_properties(bpy.types.Panel):
if show_uvedit:
col = layout.column()
- col.prop(uvedit, "cursor_location")
+ col.label("Cursor Location")
+ row = col.row()
+ row.prop(uvedit, "cursor_location", text="")
col = layout.column()
col.label(text="UVs:")
@@ -638,7 +666,7 @@ class IMAGE_PT_paint(bpy.types.Panel):
col.prop(brush, "blend", text="Blend")
- if brush.imagepaint_tool == 'CLONE':
+ if brush.image_tool == 'CLONE':
col.separator()
col.prop(brush, "clone_image", text="Image")
col.prop(brush, "clone_alpha", text="Alpha")
@@ -654,11 +682,9 @@ class IMAGE_PT_tools_brush_texture(BrushButtonsPanel, bpy.types.Panel):
toolsettings = context.tool_settings.image_paint
brush = toolsettings.brush
-# tex_slot = brush.texture_slot
-
col = layout.column()
-
col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8)
+ col.prop(brush, "use_fixed_texture")
class IMAGE_PT_tools_brush_tool(BrushButtonsPanel, bpy.types.Panel):
@@ -672,13 +698,13 @@ class IMAGE_PT_tools_brush_tool(BrushButtonsPanel, bpy.types.Panel):
col = layout.column(align=True)
- col.prop(brush, "imagepaint_tool", expand=False, text="")
+ col.prop(brush, "image_tool", expand=False, text="")
row = layout.row(align=True)
row.prop(brush, "use_paint_sculpt", text="", icon='SCULPTMODE_HLT')
row.prop(brush, "use_paint_vertex", text="", icon='VPAINT_HLT')
row.prop(brush, "use_paint_weight", text="", icon='WPAINT_HLT')
- row.prop(brush, "use_paint_texture", text="", icon='TPAINT_HLT')
+ row.prop(brush, "use_paint_image", text="", icon='TPAINT_HLT')
class IMAGE_PT_paint_stroke(BrushButtonsPanel, bpy.types.Panel):
@@ -725,13 +751,5 @@ class IMAGE_PT_paint_curve(BrushButtonsPanel, bpy.types.Panel):
row.operator("brush.curve_preset", icon="LINCURVE", text="").shape = 'LINE'
row.operator("brush.curve_preset", icon="NOCURVE", text="").shape = 'MAX'
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index 6c392cb9a3d..1fb2e5b735e 100644
--- a/release/scripts/ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -26,9 +26,7 @@ class INFO_HT_header(bpy.types.Header):
def draw(self, context):
layout = self.layout
- wm = context.window_manager
window = context.window
- sinfo = context.space_data
scene = context.scene
rd = scene.render
@@ -70,6 +68,7 @@ class INFO_HT_header(bpy.types.Header):
# XXX: BEFORE RELEASE, MOVE FILE MENU OUT OF INFO!!!
"""
+ sinfo = context.space_data
row = layout.row(align=True)
row.prop(sinfo, "show_report_debug", text="Debug")
row.prop(sinfo, "show_report_info", text="Info")
@@ -203,6 +202,7 @@ class INFO_MT_mesh_add(bpy.types.Menu):
layout.separator()
layout.operator("mesh.primitive_grid_add", icon='MESH_GRID', text="Grid")
layout.operator("mesh.primitive_monkey_add", icon='MESH_MONKEY', text="Monkey")
+ layout.operator("mesh.primitive_torus_add", text="Torus", icon='MESH_TORUS')
class INFO_MT_curve_add(bpy.types.Menu):
@@ -327,8 +327,6 @@ class INFO_MT_render(bpy.types.Menu):
def draw(self, context):
layout = self.layout
- # rd = context.scene.render
-
layout.operator("render.render", text="Render Image", icon='RENDER_STILL')
layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION').animation = True
@@ -352,7 +350,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-256-beta/'
+ layout.operator("wm.url_open", text="Release Log", icon='URL').url = 'http://www.blender.org/development/release-logs/blender-257/'
layout.separator()
@@ -363,16 +361,17 @@ class INFO_MT_help(bpy.types.Menu):
layout.separator()
layout.operator("wm.url_open", text="Report a Bug", icon='URL').url = 'http://projects.blender.org/tracker/?atid=498&group_id=9&func=browse'
layout.separator()
- layout.operator("wm.url_open", text="Python API Reference", icon='URL').url = "http://www.blender.org/documentation/blender_python_api_%s/contents.html" % "_".join(str(v) for v in bpy.app.version)
+
+ layout.operator("wm.url_open", text="Python API Reference", icon='URL').url = bpy.types.WM_OT_doc_view._prefix
layout.operator("help.operator_cheat_sheet", icon='TEXT')
layout.operator("wm.sysinfo", icon='TEXT')
layout.separator()
- if sys.platform == "win32":
- layout.operator("wm.toggle_console", icon='CONSOLE')
+ if sys.platform[:3] == "win":
+ layout.operator("wm.console_toggle", icon='CONSOLE')
layout.separator()
- layout.operator("anim.update_data_paths", text="FCurve/Driver 2.54 fix", icon='HELP')
+ layout.operator("anim.update_data_paths", text="FCurve/Driver Version fix", icon='HELP')
layout.separator()
- layout.operator("wm.splash")
+ layout.operator("wm.splash", icon='BLENDER')
# Help operators
@@ -402,13 +401,5 @@ class HELP_OT_operator_cheat_sheet(bpy.types.Operator):
self.report({'INFO'}, "See OperatorList.txt textblock")
return {'FINISHED'}
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_logic.py b/release/scripts/startup/bl_ui/space_logic.py
index 54dde61e90a..7f7aba71a46 100644
--- a/release/scripts/ui/space_logic.py
+++ b/release/scripts/startup/bl_ui/space_logic.py
@@ -66,8 +66,6 @@ class LOGIC_HT_header(bpy.types.Header):
def draw(self, context):
layout = self.layout
- st = context.space_data
-
row = layout.row(align=True)
row.template_header()
@@ -88,13 +86,5 @@ class LOGIC_MT_view(bpy.types.Menu):
layout.operator("logic.properties", icon='MENU_PANEL')
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py
index 13afc97f913..717adb3baa8 100644
--- a/release/scripts/ui/space_nla.py
+++ b/release/scripts/startup/bl_ui/space_nla.py
@@ -25,7 +25,7 @@ class NLA_HT_header(bpy.types.Header):
bl_space_type = 'NLA_EDITOR'
def draw(self, context):
- from space_dopesheet import dopesheet_filter
+ from bl_ui.space_dopesheet import dopesheet_filter
layout = self.layout
@@ -93,6 +93,10 @@ class NLA_MT_select(bpy.types.Menu):
layout.operator("nla.select_border")
layout.operator("nla.select_border", text="Border Axis Range").axis_range = True
+ layout.separator()
+ layout.operator("nla.select_leftright", text="Before Current Frame").mode = 'LEFT'
+ layout.operator("nla.select_leftright", text="After Current Frame").mode = 'RIGHT'
+
class NLA_MT_marker(bpy.types.Menu):
bl_label = "Marker"
@@ -184,15 +188,7 @@ class NLA_MT_edit_transform(bpy.types.Menu):
layout.column()
layout.operator("transform.translate", text="Grab/Move")
layout.operator("transform.transform", text="Extend").mode = 'TIME_EXTEND'
- layout.operator("transform.resize", text="Scale")
-
+ layout.operator("transform.transform", text="Scale").mode = 'TIME_SCALE'
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index aaf14813ee5..fed1cc49c4c 100644
--- a/release/scripts/ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -156,9 +156,6 @@ class NODE_MT_node(bpy.types.Menu):
layout.operator("node.preview_toggle")
layout.operator("node.hide_socket_toggle")
- # XXX
- # layout.operator("node.rename")
-
layout.separator()
layout.operator("node.show_cyclic_dependencies")
@@ -195,13 +192,5 @@ class NODE_PT_properties(bpy.types.Panel):
col.prop(snode, "backdrop_y", text="Y")
col.operator("node.backimage_move", text="Move")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index 219dd1de117..1f196cbd191 100644
--- a/release/scripts/ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -72,7 +72,7 @@ class OUTLINER_MT_view(bpy.types.Menu):
space = context.space_data
col = layout.column()
- if space.display_mode not in ('DATABLOCKS', 'USER_PREFERENCES', 'KEYMAPS'):
+ if space.display_mode not in {'DATABLOCKS', 'USER_PREFERENCES', 'KEYMAPS'}:
col.prop(space, "show_restrict_columns")
col.separator()
col.operator("outliner.show_active")
@@ -116,13 +116,5 @@ class OUTLINER_MT_edit_datablocks(bpy.types.Menu):
col.operator("outliner.drivers_add_selected")
col.operator("outliner.drivers_delete_selected")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 3ae1e35aaf8..2079aef6402 100644
--- a/release/scripts/ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -101,34 +101,6 @@ class SEQUENCER_MT_view(bpy.types.Menu):
layout.separator()
- """
- uiBlock *block= uiBeginBlock(C, ar, "seq_viewmenu", UI_EMBOSSP);
- short yco= 0, menuwidth=120;
-
- if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Play Back Animation "
- "in all Sequence Areas|Alt A", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
- }
- else {
- uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL,
- "Grease Pencil...", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, 7, "");
- uiDefMenuSep(block);
-
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Play Back Animation "
- "in this window|Alt A", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
- }
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Play Back Animation in all "
- "3D Views and Sequence Areas|Alt Shift A",
- 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
-
- """
if (st.view_type == 'SEQUENCER') or (st.view_type == 'SEQUENCER_PREVIEW'):
layout.operator("sequencer.view_all", text='View all Sequences')
if (st.view_type == 'PREVIEW') or (st.view_type == 'SEQUENCER_PREVIEW'):
@@ -353,7 +325,6 @@ class SEQUENCER_PT_edit(SequencerButtonsPanel, bpy.types.Panel):
def draw(self, context):
layout = self.layout
scene = context.scene
- render = context.scene.render
frame_current = scene.frame_current
strip = act_strip(context)
@@ -416,11 +387,11 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, bpy.types.Panel):
if not strip:
return False
- return strip.type in ('ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
+ return strip.type in {'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
'PLUGIN',
'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED',
- 'MULTICAM')
+ 'MULTICAM'}
def draw(self, context):
layout = self.layout
@@ -446,7 +417,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, bpy.types.Panel):
col = layout.column()
col.prop(strip, "blur_width", slider=True)
- if strip.transition_type in ('SINGLE', 'DOUBLE'):
+ if strip.transition_type in {'SINGLE', 'DOUBLE'}:
col.prop(strip, "angle")
elif strip.type == 'GLOW':
@@ -492,7 +463,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, bpy.types.Panel):
col = layout.column(align=True)
if strip.type == 'SPEED':
col.prop(strip, "multiply_speed")
- elif strip.type in ('CROSS', 'GAMMA_CROSS', 'PLUGIN', 'WIPE'):
+ elif strip.type in {'CROSS', 'GAMMA_CROSS', 'PLUGIN', 'WIPE'}:
col.prop(strip, "use_default_fade", "Default fade")
if not strip.use_default_fade:
col.prop(strip, "effect_fader", text="Effect fader")
@@ -554,12 +525,12 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, bpy.types.Panel):
if not strip:
return False
- return strip.type in ('MOVIE', 'IMAGE', 'SCENE', 'META',
+ return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META',
'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
'PLUGIN',
'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
- 'MULTICAM', 'SPEED')
+ 'MULTICAM', 'SPEED'}
def draw(self, context):
layout = self.layout
@@ -704,12 +675,12 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, bpy.types.Panel):
if not strip:
return False
- return strip.type in ('MOVIE', 'IMAGE', 'SCENE', 'META',
+ return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META',
'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP',
'PLUGIN',
'WIPE', 'GLOW', 'TRANSFORM', 'COLOR',
- 'MULTICAM', 'SPEED')
+ 'MULTICAM', 'SPEED'}
def draw(self, context):
layout = self.layout
@@ -766,7 +737,7 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel, bpy.types.Panel):
if not strip:
return False
- return strip.type in ('MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM')
+ return strip.type in {'MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM'}
def draw_header(self, context):
strip = act_strip(context)
@@ -829,13 +800,5 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output, bpy.types.Panel):
col.prop(st, "show_separate_color")
col.prop(st, "proxy_render_size")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py
index df5967c9128..5b07e8dc37f 100644
--- a/release/scripts/ui/space_text.py
+++ b/release/scripts/startup/bl_ui/space_text.py
@@ -95,6 +95,11 @@ class TEXT_PT_properties(bpy.types.Panel):
if text:
flow.prop(text, "use_tabs_as_spaces")
+ flow.prop(st, "show_margin")
+ col = flow.column()
+ col.active = st.show_margin
+ col.prop(st, "margin_column")
+
class TEXT_PT_find(bpy.types.Panel):
bl_space_type = 'TEXT_EDITOR'
@@ -124,6 +129,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")
@@ -293,13 +299,5 @@ class TEXT_MT_toolbox(bpy.types.Menu):
layout.operator("text.run_script")
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 046274abc89..7e62465d1ee 100644
--- a/release/scripts/ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -141,7 +141,6 @@ class TIME_MT_frame(bpy.types.Menu):
def draw(self, context):
layout = self.layout
- # tools = context.tool_settings
layout.operator("marker.add", text="Add Marker")
layout.operator("marker.duplicate", text="Duplicate Marker")
@@ -198,13 +197,5 @@ class TIME_MT_autokey(bpy.types.Menu):
layout.prop_enum(tools, "auto_keying_mode", 'ADD_REPLACE_KEYS')
layout.prop_enum(tools, "auto_keying_mode", 'REPLACE_KEYS')
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 79bdff8ad93..bf396e98c79 100644
--- a/release/scripts/ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -19,7 +19,9 @@
# <pep8 compliant>
import bpy
import os
-import shutil
+import addon_utils
+
+from bpy.props import StringProperty, BoolProperty, EnumProperty
def ui_items_general(col, context):
@@ -118,6 +120,18 @@ class USERPREF_MT_interaction_presets(bpy.types.Menu):
draw = bpy.types.Menu.draw_preset
+class USERPREF_MT_appconfigs(bpy.types.Menu):
+ bl_label = "AppPresets"
+ preset_subdir = "keyconfig"
+ preset_operator = "wm.appconfig_activate"
+
+ def draw(self, context):
+ props = self.layout.operator("wm.appconfig_default", text="Blender (default)")
+
+ # now draw the presets
+ bpy.types.Menu.draw_preset(self, context)
+
+
class USERPREF_MT_splash(bpy.types.Menu):
bl_label = "Splash"
@@ -132,7 +146,7 @@ class USERPREF_MT_splash(bpy.types.Menu):
# text = bpy.path.display_name(context.window_manager.keyconfigs.active.name)
# if not text:
# text = "Blender (default)"
- row.menu("USERPREF_MT_keyconfigs", text="Preset")
+ row.menu("USERPREF_MT_appconfigs", text="Preset")
class USERPREF_PT_interface(bpy.types.Panel):
@@ -635,7 +649,29 @@ class USERPREF_PT_theme(bpy.types.Panel):
layout.separator()
layout.separator()
+ elif theme.theme_area == 'BONE_COLOR_SETS':
+ col = split.column()
+
+ for i, ui in enumerate(theme.bone_color_sets):
+ col.label(text="Color Set %d:" % (i + 1)) # i starts from 0
+
+ row = col.row()
+
+ subsplit = row.split(percentage=0.95)
+
+ padding = subsplit.split(percentage=0.15)
+ colsub = padding.column()
+ colsub = padding.column()
+ colsub.row().prop(ui, "normal")
+ colsub.row().prop(ui, "select")
+ colsub.row().prop(ui, "active")
+
+ subsplit = row.split(percentage=0.85)
+ padding = subsplit.split(percentage=0.15)
+ colsub = padding.column()
+ colsub = padding.column()
+ colsub.row().prop(ui, "show_colored_constraints")
else:
self._theme_generic(split, getattr(theme, theme.theme_area.lower()))
@@ -713,7 +749,7 @@ class USERPREF_PT_file(bpy.types.Panel):
sub.active = paths.use_auto_save_temporary_files
sub.prop(paths, "auto_save_time", text="Timer (mins)")
-from space_userpref_keymap import InputKeyMapPanel
+from bl_ui.space_userpref_keymap import InputKeyMapPanel
class USERPREF_PT_input(InputKeyMapPanel):
@@ -764,9 +800,9 @@ class USERPREF_PT_input(InputKeyMapPanel):
sub.label(text="Zoom Style:")
sub.row().prop(inputs, "view_zoom_method", text="")
- if inputs.view_zoom_method == 'DOLLY':
+ if inputs.view_zoom_method in {'DOLLY', 'CONTINUE'}:
sub.row().prop(inputs, "view_zoom_axis", expand=True)
- sub.prop(inputs, "invert_mouse_wheel_zoom")
+ sub.prop(inputs, "invert_mouse_zoom")
#sub.prop(inputs, "use_mouse_mmb_paste")
@@ -795,7 +831,6 @@ class USERPREF_PT_input(InputKeyMapPanel):
#start = time.time()
userpref = context.user_preferences
- wm = context.window_manager
inputs = userpref.inputs
@@ -830,8 +865,6 @@ class USERPREF_PT_addons(bpy.types.Panel):
bl_region_type = 'WINDOW'
bl_options = {'HIDE_HEADER'}
- _addons_cats = None
- _addons_sups = None
_addons_fake_modules = {}
@classmethod
@@ -843,107 +876,6 @@ class USERPREF_PT_addons(bpy.types.Panel):
def module_get(mod_name):
return USERPREF_PT_addons._addons_fake_modules[mod_name]
- @staticmethod
- def _addon_list():
- import os
- import sys
- import time
-
- modules = []
- loaded_modules = set()
-
- # RELEASE SCRIPTS: official scripts distributed in Blender releases
- paths = bpy.utils.script_paths("addons")
-
- # CONTRIB SCRIPTS: good for testing but not official scripts yet
- # if folder addons_contrib/ exists, scripts in there will be loaded too
- paths += bpy.utils.script_paths("addons_contrib")
-
- # EXTERN SCRIPTS: external projects scripts
- # if folder addons_extern/ exists, scripts in there will be loaded too
- paths += bpy.utils.script_paths("addons_extern")
-
- if bpy.app.debug:
- t_main = time.time()
-
- # fake module importing
- def fake_module(mod_name, mod_path, speedy=True):
- if bpy.app.debug:
- print("fake_module", mod_name, mod_path)
- import ast
- ModuleType = type(ast)
- file_mod = open(mod_path, "r", encoding='UTF-8')
- if speedy:
- lines = []
- line_iter = iter(file_mod)
- l = ""
- while not l.startswith("bl_info"):
- l = line_iter.readline()
- if len(l) == 0:
- break
- while l.rstrip():
- lines.append(l)
- l = line_iter.readline()
- data = "".join(lines)
-
- else:
- data = file_mod.read()
-
- file_mod.close()
-
- try:
- ast_data = ast.parse(data, filename=mod_path)
- except:
- print("Syntax error 'ast.parse' can't read %r" % mod_path)
- import traceback
- traceback.print_exc()
- ast_data = None
-
- body_info = None
-
- if ast_data:
- for body in ast_data.body:
- if body.__class__ == ast.Assign:
- if len(body.targets) == 1:
- if getattr(body.targets[0], "id", "") == "bl_info":
- body_info = body
- break
-
- if body_info:
- mod = ModuleType(mod_name)
- mod.bl_info = ast.literal_eval(body.value)
- mod.__file__ = mod_path
- mod.__time__ = os.path.getmtime(mod_path)
- return mod
- else:
- return None
-
- modules_stale = set(USERPREF_PT_addons._addons_fake_modules.keys())
-
- for path in paths:
- for mod_name, mod_path in bpy.path.module_names(path):
- modules_stale -= {mod_name}
- mod = USERPREF_PT_addons._addons_fake_modules.get(mod_name)
- if mod:
- if mod.__time__ != os.path.getmtime(mod_path):
- print("reloading addon:", mod_name, mod.__time__, os.path.getmtime(mod_path), mod_path)
- del USERPREF_PT_addons._addons_fake_modules[mod_name]
- mod = None
-
- if mod is None:
- mod = fake_module(mod_name, mod_path)
- if mod:
- USERPREF_PT_addons._addons_fake_modules[mod_name] = mod
-
- # just incase we get stale modules, not likely
- for mod_stale in modules_stale:
- del USERPREF_PT_addons._addons_fake_modules[mod_stale]
- del modules_stale
-
- mod_list = list(USERPREF_PT_addons._addons_fake_modules.values())
- mod_list.sort(key=lambda mod: (mod.bl_info['category'], mod.bl_info['name']))
- return mod_list
-
def draw(self, context):
layout = self.layout
@@ -951,22 +883,7 @@ class USERPREF_PT_addons(bpy.types.Panel):
used_ext = {ext.module for ext in userpref.addons}
# collect the categories that can be filtered on
- addons = [(mod, addon_info_get(mod)) for mod in self._addon_list()]
-
- cats = {info["category"] for mod, info in addons}
- cats.discard("")
-
- if USERPREF_PT_addons._addons_cats != cats:
- bpy.types.WindowManager.addon_filter = bpy.props.EnumProperty(items=[(cat, cat, "") for cat in ["All", "Enabled", "Disabled"] + sorted(cats)], name="Category", description="Filter add-ons by category")
- bpy.types.WindowManager.addon_search = bpy.props.StringProperty(name="Search", description="Search within the selected filter")
- USERPREF_PT_addons._addons_cats = cats
-
- sups = {info["support"] for mod, info in addons}
- sups.discard("")
-
- if USERPREF_PT_addons._addons_sups != sups:
- bpy.types.WindowManager.addon_support = bpy.props.EnumProperty(items=[(sup, sup.title(), "") for sup in reversed(sorted(sups))], name="Support", description="Display support level", default={'OFFICIAL', 'COMMUNITY'}, options={'ENUM_FLAG'})
- USERPREF_PT_addons._addons_sups = sups
+ addons = [(mod, addon_utils.module_bl_info(mod)) for mod in addon_utils.modules(USERPREF_PT_addons._addons_fake_modules)]
split = layout.split(percentage=0.2)
col = split.column()
@@ -1070,7 +987,7 @@ class USERPREF_PT_addons(bpy.types.Panel):
module_names = {mod.__name__ for mod, info in addons}
missing_modules = {ext for ext in used_ext if ext not in module_names}
- if missing_modules and filter in ("All", "Enabled"):
+ if missing_modules and filter in {"All", "Enabled"}:
col.column().separator()
col.column().label(text="Missing script files")
@@ -1088,29 +1005,6 @@ class USERPREF_PT_addons(bpy.types.Panel):
row.operator("wm.addon_disable", icon='CHECKBOX_HLT', text="", emboss=False).module = module_name
-from bpy.props import *
-
-
-def addon_info_get(mod, info_basis={"name": "", "author": "", "version": (), "blender": (), "api": 0, "location": "", "description": "", "wiki_url": "", "tracker_url": "", "support": 'COMMUNITY', "category": "", "warning": "", "show_expanded": False}):
- addon_info = getattr(mod, "bl_info", {})
-
- # avoid re-initializing
- if "_init" in addon_info:
- return addon_info
-
- if not addon_info:
- mod.bl_info = addon_info
-
- for key, value in info_basis.items():
- addon_info.setdefault(key, value)
-
- if not addon_info["name"]:
- addon_info["name"] = mod.__name__
-
- addon_info["_init"] = None
- return addon_info
-
-
class WM_OT_addon_enable(bpy.types.Operator):
"Enable an addon"
bl_idname = "wm.addon_enable"
@@ -1119,11 +1013,11 @@ class WM_OT_addon_enable(bpy.types.Operator):
module = StringProperty(name="Module", description="Module name of the addon to enable")
def execute(self, context):
- mod = bpy.utils.addon_enable(self.module)
+ mod = addon_utils.enable(self.module)
if mod:
# check if add-on is written for current blender version, or raise a warning
- info = addon_info_get(mod)
+ info = addon_utils.module_bl_info(mod)
if info.get("blender", (0, 0, 0)) > bpy.app.version:
self.report("WARNING','This script was written for a newer version of Blender and might not function (correctly).\nThe script is enabled though.")
@@ -1140,7 +1034,7 @@ class WM_OT_addon_disable(bpy.types.Operator):
module = StringProperty(name="Module", description="Module name of the addon to disable")
def execute(self, context):
- bpy.utils.addon_disable(self.module)
+ addon_utils.disable(self.module)
return {'FINISHED'}
@@ -1150,6 +1044,10 @@ class WM_OT_addon_install(bpy.types.Operator):
bl_label = "Install Add-On..."
overwrite = BoolProperty(name="Overwrite", description="Remove existing addons with the same ID", default=True)
+ target = EnumProperty(
+ name="Target Path",
+ items=(('DEFAULT', "Default", ""),
+ ('PREFS', "User Prefs", "")))
filepath = StringProperty(name="File Path", description="File path to write file to")
filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'})
@@ -1172,16 +1070,41 @@ class WM_OT_addon_install(bpy.types.Operator):
def execute(self, context):
import traceback
import zipfile
+ import shutil
+
pyfile = self.filepath
- # dont use bpy.utils.script_paths("addons") because we may not be able to write to it.
- path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
+ if self.target == 'DEFAULT':
+ # dont use bpy.utils.script_paths("addons") because we may not be able to write to it.
+ path_addons = bpy.utils.user_resource('SCRIPTS', "addons", create=True)
+ else:
+ path_addons = bpy.context.user_preferences.filepaths.script_directory
+ if path_addons:
+ path_addons = os.path.join(path_addons, "addons")
if not path_addons:
- self.report({'WARNING'}, "Failed to get addons path\n")
+ self.report({'ERROR'}, "Failed to get addons path")
return {'CANCELLED'}
- contents = set(os.listdir(path_addons))
+ # create dir is if missing.
+ if not os.path.exists(path_addons):
+ os.makedirs(path_addons)
+
+ # Check if we are installing from a target path,
+ # doing so causes 2+ addons of same name or when the same from/to
+ # location is used, removal of the file!
+ addon_path = ""
+ pyfile_dir = os.path.dirname(pyfile)
+ for addon_path in addon_utils.paths():
+ if os.path.samefile(pyfile_dir, addon_path):
+ self.report({'ERROR'}, "Source file is in the addon search path: %r" % addon_path)
+ return {'CANCELLED'}
+ del addon_path
+ 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)
if zipfile.is_zipfile(pyfile):
@@ -1203,6 +1126,12 @@ class WM_OT_addon_install(bpy.types.Operator):
try: # extract the file to "addons"
file_to_extract.extractall(path_addons)
+
+ # zip files can create this dir with metadata, don't need it
+ macosx_dir = os.path.join(path_addons, '__MACOSX')
+ if os.path.isdir(macosx_dir):
+ shutil.rmtree(macosx_dir)
+
except:
traceback.print_exc()
return {'CANCELLED'}
@@ -1224,23 +1153,28 @@ class WM_OT_addon_install(bpy.types.Operator):
traceback.print_exc()
return {'CANCELLED'}
+ addons_new = {mod.__name__ for mod in addon_utils.modules(USERPREF_PT_addons._addons_fake_modules)} - addons_old
+ addons_new.discard("modules")
+
# disable any addons we may have enabled previously and removed.
# this is unlikely but do just incase. bug [#23978]
- addons_new = set(os.listdir(path_addons)) - contents
for new_addon in addons_new:
- bpy.utils.addon_disable(os.path.splitext(new_addon)[0])
+ addon_utils.disable(new_addon)
# possible the zip contains multiple addons, we could disallow this
# but for now just use the first
- for mod in USERPREF_PT_addons._addon_list():
+ for mod in addon_utils.modules(USERPREF_PT_addons._addons_fake_modules):
if mod.__name__ in addons_new:
- info = addon_info_get(mod)
+ info = addon_utils.module_bl_info(mod)
# show the newly installed addon.
context.window_manager.addon_filter = 'All'
context.window_manager.addon_search = info["name"]
break
+ # incase a new module path was created to install this addon.
+ bpy.utils.refresh_script_paths()
+
# TODO, should not be a warning.
# self.report({'WARNING'}, "File installed to '%s'\n" % path_dest)
return {'FINISHED'}
@@ -1270,17 +1204,9 @@ class WM_OT_addon_expand(bpy.types.Operator):
traceback.print_exc()
return {'CANCELLED'}
- info = addon_info_get(mod)
+ info = addon_utils.module_bl_info(mod)
info["show_expanded"] = not info["show_expanded"]
return {'FINISHED'}
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_userpref_keymap.py b/release/scripts/startup/bl_ui/space_userpref_keymap.py
index a78ef1be36f..378fe231091 100644
--- a/release/scripts/ui/space_userpref_keymap.py
+++ b/release/scripts/startup/bl_ui/space_userpref_keymap.py
@@ -206,7 +206,7 @@ class InputKeyMapPanel(bpy.types.Panel):
# Key Map items
if km.show_expanded_items:
- for kmi in km.items:
+ for kmi in km.keymap_items:
self.draw_kmi(display_keymaps, kc, km, kmi, col, level + 1)
# "Add New" at end of keymap item list
@@ -292,7 +292,7 @@ class InputKeyMapPanel(bpy.types.Panel):
box.enabled = km.is_user_defined
- if map_type not in ('TEXTINPUT', 'TIMER'):
+ if map_type not in {'TEXTINPUT', 'TIMER'}:
split = box.split(percentage=0.4)
sub = split.row()
@@ -339,7 +339,7 @@ class InputKeyMapPanel(bpy.types.Panel):
km = km.active()
layout.context_pointer_set("keymap", km)
- filtered_items = [kmi for kmi in km.items if filter_text in kmi.name.lower()]
+ filtered_items = [kmi for kmi in km.keymap_items if filter_text in kmi.name.lower()]
if len(filtered_items) != 0:
col = layout.column()
@@ -404,7 +404,7 @@ class InputKeyMapPanel(bpy.types.Panel):
self.draw_hierarchy(display_keymaps, col)
-from bpy.props import *
+from bpy.props import StringProperty, BoolProperty, IntProperty
def export_properties(prefix, properties, lines=None):
@@ -433,9 +433,9 @@ class WM_OT_keyconfig_test(bpy.types.Operator):
def kmistr(kmi):
if km.is_modal:
- s = ["kmi = km.items.new_modal(\'%s\', \'%s\', \'%s\'" % (kmi.propvalue, kmi.type, kmi.value)]
+ s = ["kmi = km.keymap_items.new_modal(\'%s\', \'%s\', \'%s\'" % (kmi.propvalue, kmi.type, kmi.value)]
else:
- s = ["kmi = km.items.new(\'%s\', \'%s\', \'%s\'" % (kmi.idname, kmi.type, kmi.value)]
+ s = ["kmi = km.keymap_items.new(\'%s\', \'%s\', \'%s\'" % (kmi.idname, kmi.type, kmi.value)]
if kmi.any:
s.append(", any=True")
@@ -468,7 +468,7 @@ class WM_OT_keyconfig_test(bpy.types.Operator):
km = km.active()
if src:
- for item in km.items:
+ for item in km.keymap_items:
if src.compare(item):
print("===========")
print(parent.name)
@@ -481,15 +481,15 @@ class WM_OT_keyconfig_test(bpy.types.Operator):
if self.testEntry(kc, child, src, parent):
result = True
else:
- for i in range(len(km.items)):
- src = km.items[i]
+ for i in range(len(km.keymap_items)):
+ src = km.keymap_items[i]
for child in children:
if self.testEntry(kc, child, src, km):
result = True
- for j in range(len(km.items) - i - 1):
- item = km.items[j + i + 1]
+ for j in range(len(km.keymap_items) - i - 1):
+ item = km.keymap_items[j + i + 1]
if src.compare(item):
print("===========")
print(km.name)
@@ -555,13 +555,7 @@ class WM_OT_keyconfig_import(bpy.types.Operator):
config_name = basename(self.filepath)
- path = bpy.utils.preset_paths("keyconfig")[0] # we need some way to tell the user and system preset path
- print(path)
-
- # create config folder if needed
- if not os.path.exists(path):
- os.mkdir(path)
-
+ path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", "keyconfig"), create=True)
path = os.path.join(path, config_name)
if self.keep_original:
@@ -633,11 +627,11 @@ class WM_OT_keyconfig_export(bpy.types.Operator):
f.write("# Map %s\n" % km.name)
f.write("km = kc.keymaps.new('%s', space_type='%s', region_type='%s', modal=%s)\n\n" % (km.name, km.space_type, km.region_type, km.is_modal))
- for kmi in km.items:
+ for kmi in km.keymap_items:
if km.is_modal:
- f.write("kmi = km.items.new_modal('%s', '%s', '%s'" % (kmi.propvalue, kmi.type, kmi.value))
+ f.write("kmi = km.keymap_items.new_modal('%s', '%s', '%s'" % (kmi.propvalue, kmi.type, kmi.value))
else:
- f.write("kmi = km.items.new('%s', '%s', '%s'" % (kmi.idname, kmi.type, kmi.value))
+ f.write("kmi = km.keymap_items.new('%s', '%s', '%s'" % (kmi.idname, kmi.type, kmi.value))
if kmi.any:
f.write(", any=True")
else:
@@ -676,7 +670,6 @@ class WM_OT_keymap_edit(bpy.types.Operator):
bl_label = "Edit Key Map"
def execute(self, context):
- wm = context.window_manager
km = context.keymap
km.copy_to_user()
return {'FINISHED'}
@@ -711,13 +704,12 @@ class WM_OT_keyitem_restore(bpy.types.Operator):
@classmethod
def poll(cls, context):
- km = context.keymap
- return km.is_user_defined
+ keymap = getattr(context, "keymap", None)
+ return keymap and keymap.is_user_defined
def execute(self, context):
- wm = context.window_manager
km = context.keymap
- kmi = km.items.from_id(self.item_id)
+ kmi = km.keymap_items.from_id(self.item_id)
if not kmi.is_user_defined:
km.restore_item_to_default(kmi)
@@ -736,9 +728,9 @@ class WM_OT_keyitem_add(bpy.types.Operator):
kc = wm.keyconfigs.default
if km.is_modal:
- km.items.new_modal("", 'A', 'PRESS') # kmi
+ km.keymap_items.new_modal("", 'A', 'PRESS') # kmi
else:
- km.items.new("none", 'A', 'PRESS') # kmi
+ km.keymap_items.new("none", 'A', 'PRESS') # kmi
# clear filter and expand keymap so we can see the newly added item
if context.space_data.filter_text != "":
@@ -758,13 +750,12 @@ class WM_OT_keyitem_remove(bpy.types.Operator):
@classmethod
def poll(cls, context):
- km = context.keymap
- return km.is_user_defined
+ return hasattr(context, "keymap") and context.keymap.is_user_defined
def execute(self, context):
km = context.keymap
- kmi = km.items.from_id(self.item_id)
- km.items.remove(kmi)
+ kmi = km.keymap_items.from_id(self.item_id)
+ km.keymap_items.remove(kmi)
return {'FINISHED'}
@@ -776,22 +767,14 @@ class WM_OT_keyconfig_remove(bpy.types.Operator):
@classmethod
def poll(cls, context):
wm = context.window_manager
- return wm.keyconfigs.active.is_user_defined
+ keyconf = wm.keyconfigs.active
+ return keyconf and keyconf.is_user_defined
def execute(self, context):
- import sys
wm = context.window_manager
keyconfig = wm.keyconfigs.active
wm.keyconfigs.remove(keyconfig)
return {'FINISHED'}
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 162322a8720..7d31bc39b0a 100644
--- a/release/scripts/ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -42,13 +42,13 @@ class VIEW3D_HT_header(bpy.types.Header):
sub.menu("VIEW3D_MT_view")
# Select Menu
- if mode_string not in ('EDIT_TEXT', 'SCULPT', 'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'):
+ if mode_string not in {'EDIT_TEXT', 'SCULPT', 'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
sub.menu("VIEW3D_MT_select_%s" % mode_string.lower())
if edit_object:
sub.menu("VIEW3D_MT_edit_%s" % edit_object.type.lower())
elif obj:
- if mode_string not in ('PAINT_TEXTURE'):
+ if mode_string not in {'PAINT_TEXTURE'}:
sub.menu("VIEW3D_MT_%s" % mode_string.lower())
else:
sub.menu("VIEW3D_MT_object")
@@ -71,11 +71,11 @@ class VIEW3D_HT_header(bpy.types.Header):
row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True, toggle=True)
# Occlude geometry
- if view.viewport_shade in ('SOLID', 'SHADED', 'TEXTURED') and (obj.mode == 'PARTICLE_EDIT' or (obj.mode == 'EDIT' and obj.type == 'MESH')):
+ if view.viewport_shade in {'SOLID', 'SHADED', 'TEXTURED'} and (obj.mode == 'PARTICLE_EDIT' or (obj.mode == 'EDIT' and obj.type == 'MESH')):
row.prop(view, "use_occlude_geometry", text="")
# Proportional editing
- if obj.mode in ('EDIT', 'PARTICLE_EDIT'):
+ if obj.mode in {'EDIT', 'PARTICLE_EDIT'}:
row = layout.row(align=True)
row.prop(toolsettings, "proportional_edit", text="", icon_only=True)
if toolsettings.proportional_edit != 'DISABLED':
@@ -160,7 +160,7 @@ class VIEW3D_MT_transform(bpy.types.Menu):
layout.separator()
obj = context.object
- if obj.type == 'ARMATURE' and obj.mode in ('EDIT', 'POSE') and obj.data.draw_type in ('BBONE', 'ENVELOPE'):
+ if obj.type == 'ARMATURE' and obj.mode in {'EDIT', 'POSE'} and obj.data.draw_type in {'BBONE', 'ENVELOPE'}:
layout.operator("transform.transform", text="Scale Envelope/BBone").mode = 'BONE_SIZE'
if context.edit_object and context.edit_object.type == 'ARMATURE':
@@ -177,6 +177,11 @@ class VIEW3D_MT_transform(bpy.types.Menu):
layout.operator("object.origin_set", text="Origin to Geometry").type = 'ORIGIN_GEOMETRY'
layout.operator("object.origin_set", text="Origin to 3D Cursor").type = 'ORIGIN_CURSOR'
+ layout.separator()
+
+ layout.operator("object.randomize_transform")
+ layout.operator("object.align")
+
class VIEW3D_MT_mirror(bpy.types.Menu):
bl_label = "Mirror"
@@ -240,9 +245,21 @@ class VIEW3D_MT_uv_map(bpy.types.Menu):
layout = self.layout
layout.operator("uv.unwrap")
+
+ layout.operator_context = 'INVOKE_DEFAULT'
+ layout.operator("uv.smart_project")
+ layout.operator("uv.lightmap_pack")
+ layout.operator("uv.follow_active_quads")
+
+ layout.separator()
+
+ layout.operator_context = 'EXEC_DEFAULT'
layout.operator("uv.cube_project")
layout.operator("uv.cylinder_project")
layout.operator("uv.sphere_project")
+
+ layout.separator()
+
layout.operator("uv.project_from_view")
layout.operator("uv.project_from_view", text="Project from View (Bounds)").scale_to_bounds = True
@@ -250,6 +267,7 @@ class VIEW3D_MT_uv_map(bpy.types.Menu):
layout.operator("uv.reset")
+
# ********** View menus **********
@@ -317,16 +335,17 @@ class VIEW3D_MT_view_navigation(bpy.types.Menu):
def draw(self, context):
layout = self.layout
- layout.operator_enums("view3d.view_orbit", "type")
+ layout.operator_enum("view3d.view_orbit", "type")
layout.separator()
- layout.operator_enums("view3d.view_pan", "type")
+ layout.operator_enum("view3d.view_pan", "type")
layout.separator()
layout.operator("view3d.zoom", text="Zoom In").delta = 1
layout.operator("view3d.zoom", text="Zoom Out").delta = -1
+ layout.operator("view3d.zoom_camera_1_to_1", text="Zoom Camera 1:1")
layout.separator()
@@ -425,6 +444,7 @@ class VIEW3D_MT_select_pose(bpy.types.Menu):
layout.operator("pose.select_all", text="Select/Deselect All")
layout.operator("pose.select_inverse", text="Inverse")
+ layout.operator("pose.select_flip_active", text="Flip Active")
layout.operator("pose.select_constraint_target", text="Constraint Target")
layout.operator("pose.select_linked", text="Linked")
@@ -647,10 +667,11 @@ class VIEW3D_MT_select_face(bpy.types.Menu): # XXX no matching enum
bl_label = "Select"
def draw(self, context):
- layout = self.layout
+ # layout = self.layout
# TODO
# see view3d_select_faceselmenu
+ pass
# ********** Object menu **********
@@ -713,7 +734,6 @@ class VIEW3D_MT_object(bpy.types.Menu):
class VIEW3D_MT_object_animation(bpy.types.Menu):
- bl_context = "objectmode"
bl_label = "Animation"
def draw(self, context):
@@ -763,7 +783,7 @@ class VIEW3D_MT_object_specials(bpy.types.Menu):
props.data_path_item = "data.dof_distance"
props.input_scale = 0.02
- if obj.type in ('CURVE', 'FONT'):
+ if obj.type in {'CURVE', 'FONT'}:
layout.operator_context = 'INVOKE_REGION_WIN'
props = layout.operator("wm.context_modal_mouse", text="Extrude Size")
@@ -791,7 +811,7 @@ class VIEW3D_MT_object_specials(bpy.types.Menu):
props.data_path_iter = "selected_editable_objects"
props.data_path_item = "data.energy"
- if obj.data.type in ('SPOT', 'AREA', 'POINT'):
+ if obj.data.type in {'SPOT', 'AREA', 'POINT'}:
props = layout.operator("wm.context_modal_mouse", text="Falloff Distance")
props.data_path_iter = "selected_editable_objects"
props.data_path_item = "data.distance"
@@ -831,10 +851,15 @@ class VIEW3D_MT_object_apply(bpy.types.Menu):
def draw(self, context):
layout = self.layout
- layout.operator("object.location_apply", text="Location")
- layout.operator("object.rotation_apply", text="Rotation")
- layout.operator("object.scale_apply", text="Scale")
+ layout.operator("object.transform_apply", text="Location").location = True
+ layout.operator("object.transform_apply", text="Rotation").rotation = True
+ layout.operator("object.transform_apply", text="Scale").scale = True
+ props = layout.operator("object.transform_apply", text="Rotation & Scale")
+ props.scale = True
+ props.rotation = True
+
layout.separator()
+
layout.operator("object.visual_transform_apply", text="Visual Transform")
layout.operator("object.duplicates_make_real")
@@ -932,7 +957,7 @@ class VIEW3D_MT_make_links(bpy.types.Menu):
layout.operator_menu_enum("object.make_links_scene", "scene", text="Objects to Scene...")
layout.operator_menu_enum("marker.make_links_scene", "scene", text="Markers to Scene...")
- layout.operator_enums("object.make_links_data", "type") # inline
+ layout.operator_enum("object.make_links_data", "type") # inline
class VIEW3D_MT_object_game(bpy.types.Menu):
@@ -1038,6 +1063,10 @@ class VIEW3D_MT_paint_weight(bpy.types.Menu):
layout.operator("object.vertex_group_clean", text="Clean")
layout.operator("object.vertex_group_levels", text="Levels")
+ layout.separator()
+
+ layout.operator("paint.weight_set")
+
# ********** Sculpt menu **********
@@ -1070,12 +1099,9 @@ class VIEW3D_MT_sculpt(bpy.types.Menu):
sculpt_tool = brush.sculpt_tool
if sculpt_tool != 'GRAB':
- layout.prop(brush, "use_airbrush")
-
- if sculpt_tool != 'LAYER':
- layout.prop(brush, "use_anchor")
+ layout.prop_menu_enum(brush, "stroke_method")
- if sculpt_tool in ('DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'):
+ if sculpt_tool in {'DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'}:
layout.prop_menu_enum(brush, "direction")
if sculpt_tool == 'LAYER':
@@ -1154,31 +1180,26 @@ class VIEW3D_MT_pose(bpy.types.Menu):
def draw(self, context):
layout = self.layout
- arm = context.active_object.data
-
layout.operator("ed.undo")
layout.operator("ed.redo")
layout.separator()
layout.menu("VIEW3D_MT_transform")
- layout.menu("VIEW3D_MT_snap")
layout.menu("VIEW3D_MT_pose_transform")
+ layout.menu("VIEW3D_MT_pose_apply")
- layout.separator()
-
- layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
- layout.operator("anim.keyframe_delete_v3d", text="Delete Keyframe...")
- layout.operator("anim.keying_set_active_set", text="Change Keying Set...")
+ layout.menu("VIEW3D_MT_snap")
layout.separator()
- layout.operator("pose.relax")
+ layout.menu("VIEW3D_MT_object_animation")
layout.separator()
- layout.menu("VIEW3D_MT_pose_apply")
+ layout.menu("VIEW3D_MT_pose_slide")
+ layout.menu("VIEW3D_MT_pose_propagate")
layout.separator()
@@ -1188,7 +1209,7 @@ class VIEW3D_MT_pose(bpy.types.Menu):
layout.separator()
- layout.menu("VIEW3D_MT_pose_pose")
+ layout.menu("VIEW3D_MT_pose_library")
layout.menu("VIEW3D_MT_pose_motion")
layout.menu("VIEW3D_MT_pose_group")
@@ -1236,7 +1257,36 @@ class VIEW3D_MT_pose_transform(bpy.types.Menu):
layout.label(text="Origin")
-class VIEW3D_MT_pose_pose(bpy.types.Menu):
+class VIEW3D_MT_pose_slide(bpy.types.Menu):
+ bl_label = "In-Betweens"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("pose.push")
+ layout.operator("pose.relax")
+ layout.operator("pose.breakdown")
+
+
+class VIEW3D_MT_pose_propagate(bpy.types.Menu):
+ bl_label = "Propagate"
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("pose.propagate")
+
+ layout.separator()
+
+ layout.operator("pose.propagate", text="To Next Keyframe").mode = 'NEXT_KEY'
+ layout.operator("pose.propagate", text="To Last Keyframe (Make Cyclic)").mode = 'LAST_KEY'
+
+ layout.separator()
+
+ layout.operator("pose.propagate", text="On Selected Markers").mode = 'SELECTED_MARKERS'
+
+
+class VIEW3D_MT_pose_library(bpy.types.Menu):
bl_label = "Pose Library"
def draw(self, context):
@@ -1450,7 +1500,7 @@ class VIEW3D_OT_edit_mesh_extrude_individual_move(bpy.types.Operator):
totface = mesh.total_face_sel
totedge = mesh.total_edge_sel
- totvert = mesh.total_vert_sel
+ # totvert = mesh.total_vert_sel
if select_mode[2] and totface == 1:
bpy.ops.mesh.extrude_region_move('INVOKE_REGION_WIN', TRANSFORM_OT_translate={"constraint_orientation": 'NORMAL', "constraint_axis": (False, False, True)})
@@ -1478,7 +1528,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(bpy.types.Operator):
totface = mesh.total_face_sel
totedge = mesh.total_edge_sel
- totvert = mesh.total_vert_sel
+ # totvert = mesh.total_vert_sel
if totface >= 1:
bpy.ops.mesh.extrude_region_move('INVOKE_REGION_WIN', TRANSFORM_OT_translate={"constraint_orientation": 'NORMAL', "constraint_axis": (False, False, True)})
@@ -1510,6 +1560,8 @@ class VIEW3D_MT_edit_mesh_vertices(bpy.types.Menu):
layout.operator("mesh.vertices_smooth")
layout.operator("mesh.remove_doubles")
+ layout.operator("mesh.vertices_sort")
+ layout.operator("mesh.vertices_randomize")
layout.operator("mesh.select_vertex_path")
@@ -1938,8 +1990,7 @@ class VIEW3D_MT_edit_armature_roll(bpy.types.Menu):
def draw(self, context):
layout = self.layout
- layout.operator("armature.calculate_roll", text="Recalculate with Z-Axis Up").type = 'GLOBALUP'
- layout.operator("armature.calculate_roll", text="Recalculate with Z-Axis to Cursor").type = 'CURSOR'
+ layout.operator_menu_enum("armature.calculate_roll", "type")
layout.separator()
@@ -1962,7 +2013,6 @@ class VIEW3D_PT_view3d_properties(bpy.types.Panel):
layout = self.layout
view = context.space_data
- scene = context.scene
col = layout.column()
col.active = view.region_3d.view_perspective != 'CAMERA'
@@ -2004,7 +2054,7 @@ class VIEW3D_PT_view3d_name(bpy.types.Panel):
row.label(text="", icon='OBJECT_DATA')
row.prop(ob, "name", text="")
- if ob.type == 'ARMATURE' and ob.mode in ('EDIT', 'POSE'):
+ if ob.type == 'ARMATURE' and ob.mode in {'EDIT', 'POSE'}:
bone = context.active_bone
if bone:
row = layout.row()
@@ -2314,3 +2364,6 @@ def unregister():
if __name__ == "__main__":
register()
+
+if __name__ == "__main__": # only for live edit.
+ bpy.utils.register_module(__name__)
diff --git a/release/scripts/ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 163e02817a4..a6db6fbdde8 100644
--- a/release/scripts/ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -122,7 +122,6 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, bpy.types.Panel):
col = layout.column(align=True)
col.label(text="Deform:")
col.operator("transform.edge_slide")
- col.operator("mesh.rip_move")
col.operator("mesh.noise")
col.operator("mesh.vertices_smooth")
@@ -173,11 +172,18 @@ class VIEW3D_PT_tools_meshedit_options(View3DPanel, bpy.types.Panel):
ob = context.active_object
if ob:
- mesh = context.active_object.data
+ mesh = ob.data
col = layout.column(align=True)
col.prop(mesh, "use_mirror_x")
- col.prop(mesh, "use_mirror_topology")
- col.prop(context.tool_settings, "edge_path_mode")
+ sub = col.column()
+ sub.active = ob.data.use_mirror_x
+ sub.prop(mesh, "use_mirror_topology")
+
+ ts = context.tool_settings
+
+ col.label("Edge Select Mode")
+ col.prop(ts, "edge_path_mode", text="")
+ col.prop(context.tool_settings, "edge_path_live_unwrap")
# ********** default tools for editmode_curve ****************
@@ -324,12 +330,9 @@ class VIEW3D_PT_tools_armatureedit_options(View3DPanel, bpy.types.Panel):
bl_label = "Armature Options"
def draw(self, context):
- layout = self.layout
-
arm = context.active_object.data
- col = layout.column(align=True)
- col.prop(arm, "use_mirror_x")
+ self.layout.prop(arm, "use_mirror_x")
# ********** default tools for editmode_mball ****************
@@ -424,12 +427,9 @@ class VIEW3D_PT_tools_posemode_options(View3DPanel, bpy.types.Panel):
bl_label = "Pose Options"
def draw(self, context):
- layout = self.layout
-
arm = context.active_object.data
- col = layout.column(align=True)
- col.prop(arm, "use_auto_ik")
+ self.layout.prop(arm, "use_auto_ik")
# ********** default tools for paint modes ****************
@@ -448,7 +448,7 @@ class PaintPanel():
return ts.vertex_paint
elif context.weight_paint_object:
return ts.weight_paint
- elif context.texture_paint_object:
+ elif context.image_paint_object:
return ts.image_paint
elif context.particle_edit_object:
return ts.particle_edit
@@ -515,16 +515,16 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
row.prop(brush, "unprojected_radius", text="Radius", slider=True)
else:
row.prop(brush, "use_locked_size", toggle=True, text="", icon='UNLOCKED')
- row.prop(brush, "size", text="Radius", slider=True)
+ row.prop(brush, "size", slider=True)
row.prop(brush, "use_pressure_size", toggle=True, text="")
- if brush.sculpt_tool not in ('SNAKE_HOOK', 'GRAB', 'ROTATE'):
+ if brush.sculpt_tool not in {'SNAKE_HOOK', 'GRAB', 'ROTATE'}:
col.separator()
row = col.row(align=True)
- if brush.use_space and brush.sculpt_tool not in ('SMOOTH'):
+ if brush.use_space and brush.sculpt_tool not in {'SMOOTH'}:
if brush.use_space_atten:
row.prop(brush, "use_space_atten", toggle=True, text="", icon='LOCKED')
else:
@@ -533,26 +533,26 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
row.prop(brush, "strength", text="Strength", slider=True)
row.prop(brush, "use_pressure_strength", text="")
- if brush.sculpt_tool not in ('SMOOTH'):
+ if brush.sculpt_tool not in {'SMOOTH'}:
col.separator()
row = col.row(align=True)
row.prop(brush, "auto_smooth_factor", slider=True)
row.prop(brush, "use_inverse_smooth_pressure", toggle=True, text="")
- if brush.sculpt_tool in ('GRAB', 'SNAKE_HOOK'):
+ if brush.sculpt_tool in {'GRAB', 'SNAKE_HOOK'}:
col.separator()
row = col.row(align=True)
row.prop(brush, "normal_weight", slider=True)
- if brush.sculpt_tool in ('CREASE', 'BLOB'):
+ if brush.sculpt_tool in {'CREASE', 'BLOB'}:
col.separator()
row = col.row(align=True)
row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch")
- if brush.sculpt_tool not in ('PINCH', 'INFLATE', 'SMOOTH'):
+ if brush.sculpt_tool not in {'PINCH', 'INFLATE', 'SMOOTH'}:
row = col.row(align=True)
col.separator()
@@ -564,8 +564,8 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
row.prop(brush, "sculpt_plane", text="")
- #if brush.sculpt_tool in ('CLAY', 'CLAY_TUBES', 'FLATTEN', 'FILL', 'SCRAPE'):
- if brush.sculpt_tool in ('CLAY', 'FLATTEN', 'FILL', 'SCRAPE'):
+ #if brush.sculpt_tool in {'CLAY', 'CLAY_TUBES', 'FLATTEN', 'FILL', 'SCRAPE'}:
+ if brush.sculpt_tool in {'CLAY', 'FLATTEN', 'FILL', 'SCRAPE'}:
row = col.row(align=True)
row.prop(brush, "plane_offset", slider=True)
row.prop(brush, "use_offset_pressure", text="")
@@ -578,6 +578,10 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
row.active = brush.use_plane_trim
row.prop(brush, "plane_trim", slider=True, text="Distance")
+ if brush.sculpt_tool == 'LAYER':
+ row = col.row()
+ row.prop(brush, "height", slider=True, text="Height")
+
col.separator()
row = col.row()
@@ -586,7 +590,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
col.separator()
col.row().prop(brush, "direction", expand=True)
- if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'INFLATE', 'LAYER', 'CLAY'):
+ if brush.sculpt_tool in {'DRAW', 'CREASE', 'BLOB', 'INFLATE', 'LAYER', 'CLAY'}:
col.separator()
col.prop(brush, "use_accumulate")
@@ -608,13 +612,13 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
# Texture Paint Mode #
- elif context.texture_paint_object and brush:
+ elif context.image_paint_object and brush:
col = layout.column()
col.template_color_wheel(brush, "color", value_slider=True)
col.prop(brush, "color", text="")
row = col.row(align=True)
- row.prop(brush, "size", text="Radius", slider=True)
+ row.prop(brush, "size", slider=True)
row.prop(brush, "use_pressure_size", toggle=True, text="")
row = col.row(align=True)
@@ -628,7 +632,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
col.prop(brush, "blend", text="Blend")
col = layout.column()
- col.active = (brush.blend not in ('ERASE_ALPHA', 'ADD_ALPHA'))
+ col.active = (brush.blend not in {'ERASE_ALPHA', 'ADD_ALPHA'})
col.prop(brush, "use_alpha")
# Weight Paint Mode #
@@ -639,7 +643,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
col = layout.column()
row = col.row(align=True)
- row.prop(brush, "size", text="Radius", slider=True)
+ row.prop(brush, "size", slider=True)
row.prop(brush, "use_pressure_size", toggle=True, text="")
row = col.row(align=True)
@@ -657,7 +661,7 @@ class VIEW3D_PT_tools_brush(PaintPanel, bpy.types.Panel):
col.prop(brush, "color", text="")
row = col.row(align=True)
- row.prop(brush, "size", text="Radius", slider=True)
+ row.prop(brush, "size", slider=True)
row.prop(brush, "use_pressure_size", toggle=True, text="")
row = col.row(align=True)
@@ -678,7 +682,7 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
def poll(cls, context):
settings = cls.paint_settings(context)
return (settings and settings.brush and (context.sculpt_object or
- context.texture_paint_object))
+ context.image_paint_object))
def draw(self, context):
layout = self.layout
@@ -690,6 +694,8 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
col = layout.column()
col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8)
+ if brush.use_paint_image:
+ col.prop(brush, "use_fixed_texture")
if context.sculpt_object:
#XXX duplicated from properties_texture.py
@@ -703,32 +709,32 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
col.separator()
col = layout.column()
- col.active = tex_slot.map_mode in ('FIXED', )
+ col.active = tex_slot.map_mode in {'FIXED'}
col.label(text="Angle:")
col = layout.column()
- if not brush.use_anchor and brush.sculpt_tool not in ('GRAB', 'SNAKE_HOOK', 'THUMB', 'ROTATE') and tex_slot.map_mode in ('FIXED'):
+ if not brush.use_anchor and brush.sculpt_tool not in {'GRAB', 'SNAKE_HOOK', 'THUMB', 'ROTATE'} and tex_slot.map_mode in {'FIXED'}:
col.prop(brush, "texture_angle_source_random", text="")
else:
col.prop(brush, "texture_angle_source_no_random", text="")
#row = col.row(align=True)
#row.label(text="Angle:")
- #row.active = tex_slot.map_mode in ('FIXED', 'TILED')
+ #row.active = tex_slot.map_mode in {'FIXED', 'TILED'}
#row = col.row(align=True)
#col = row.column()
- #col.active = tex_slot.map_mode in ('FIXED')
+ #col.active = tex_slot.map_mode in {'FIXED'}
#col.prop(brush, "use_rake", toggle=True, icon='PARTICLEMODE', text="")
col = layout.column()
col.prop(tex_slot, "angle", text="")
- col.active = tex_slot.map_mode in ('FIXED', 'TILED')
+ col.active = tex_slot.map_mode in {'FIXED', 'TILED'}
#col = layout.column()
#col.prop(brush, "use_random_rotation")
- #col.active = (not brush.use_rake) and (not brush.use_anchor) and brush.sculpt_tool not in ('GRAB', 'SNAKE_HOOK', 'THUMB', 'ROTATE') and tex_slot.map_mode in ('FIXED')
+ #col.active = (not brush.use_rake) and (not brush.use_anchor) and (brush.sculpt_tool not in {'GRAB', 'SNAKE_HOOK', 'THUMB', 'ROTATE'}) and tex_slot.map_mode in {'FIXED'}
split = layout.split()
@@ -748,7 +754,7 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
row = col.row(align=True)
row.label(text="Overlay:")
- row.active = tex_slot.map_mode in ('FIXED', 'TILED')
+ row.active = tex_slot.map_mode in {'FIXED', 'TILED'}
row = col.row(align=True)
@@ -759,11 +765,11 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel, bpy.types.Panel):
else:
col.prop(brush, "use_texture_overlay", toggle=True, text="", icon='MUTE_IPO_ON')
- col.active = tex_slot.map_mode in ('FIXED', 'TILED')
+ col.active = tex_slot.map_mode in {'FIXED', 'TILED'}
col = row.column()
col.prop(brush, "texture_overlay_alpha", text="Alpha")
- col.active = tex_slot.map_mode in ('FIXED', 'TILED') and brush.use_texture_overlay
+ col.active = tex_slot.map_mode in {'FIXED', 'TILED'} and brush.use_texture_overlay
class VIEW3D_PT_tools_brush_tool(PaintPanel, bpy.types.Panel):
@@ -774,7 +780,7 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel, bpy.types.Panel):
def poll(cls, context):
settings = cls.paint_settings(context)
return (settings and settings.brush and
- (context.sculpt_object or context.texture_paint_object or
+ (context.sculpt_object or context.image_paint_object or
context.vertex_paint_object or context.weight_paint_object))
def draw(self, context):
@@ -782,24 +788,22 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel, bpy.types.Panel):
settings = __class__.paint_settings(context)
brush = settings.brush
- texture_paint = context.texture_paint_object
- sculpt = context.sculpt_object
col = layout.column(align=True)
if context.sculpt_object:
col.prop(brush, "sculpt_tool", expand=False, text="")
col.operator("brush.reset")
- elif context.texture_paint_object:
- col.prop(brush, "imagepaint_tool", expand=False, text="")
+ elif context.image_paint_object:
+ col.prop(brush, "image_tool", expand=False, text="")
elif context.vertex_paint_object or context.weight_paint_object:
- col.prop(brush, "vertexpaint_tool", expand=False, text="")
+ col.prop(brush, "vertex_tool", expand=False, text="")
row = layout.row(align=True)
row.prop(brush, "use_paint_sculpt", text="", icon='SCULPTMODE_HLT')
row.prop(brush, "use_paint_vertex", text="", icon='VPAINT_HLT')
row.prop(brush, "use_paint_weight", text="", icon='WPAINT_HLT')
- row.prop(brush, "use_paint_texture", text="", icon='TPAINT_HLT')
+ row.prop(brush, "use_paint_image", text="", icon='TPAINT_HLT')
class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
@@ -812,14 +816,14 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
return (settings and settings.brush and (context.sculpt_object or
context.vertex_paint_object or
context.weight_paint_object or
- context.texture_paint_object))
+ context.image_paint_object))
def draw(self, context):
layout = self.layout
settings = __class__.paint_settings(context)
brush = settings.brush
- texture_paint = context.texture_paint_object
+ image_paint = context.image_paint_object
col = layout.column()
@@ -843,7 +847,7 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
row.active = brush.use_space
row.prop(brush, "spacing", text="Spacing")
- if brush.sculpt_tool not in ('GRAB', 'THUMB', 'SNAKE_HOOK', 'ROTATE') and (not brush.use_anchor) and (not brush.use_restore_mesh):
+ if (brush.sculpt_tool not in {'GRAB', 'THUMB', 'SNAKE_HOOK', 'ROTATE'}) and (not brush.use_anchor) and (not brush.use_restore_mesh):
col = layout.column()
col.separator()
@@ -870,7 +874,7 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
col.separator()
- if not texture_paint:
+ if not image_paint:
row = col.row()
row.prop(brush, "use_smooth_stroke")
@@ -882,7 +886,7 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
col.separator()
col = layout.column()
- col.active = (not brush.use_anchor) and (brush.sculpt_tool not in ('GRAB', 'THUMB', 'ROTATE', 'SNAKE_HOOK'))
+ col.active = (not brush.use_anchor) and (brush.sculpt_tool not in {'GRAB', 'THUMB', 'ROTATE', 'SNAKE_HOOK'})
row = col.row()
row.prop(brush, "use_space")
@@ -896,7 +900,7 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel, bpy.types.Panel):
#col.separator()
- #if texture_paint:
+ #if image_paint:
# row.prop(brush, "use_pressure_spacing", toggle=True, text="")
@@ -941,28 +945,22 @@ class VIEW3D_PT_sculpt_options(PaintPanel, bpy.types.Panel):
tool_settings = context.tool_settings
sculpt = tool_settings.sculpt
settings = __class__.paint_settings(context)
- brush = settings.brush
-
- split = layout.split()
-
- col = split.column()
-
- col.prop(sculpt, "use_threaded", text="Threaded Sculpt")
- col.prop(sculpt, "show_low_resolution")
- col.prop(sculpt, "show_brush")
- col.label(text="Unified Settings:")
- col.prop(tool_settings, "sculpt_paint_use_unified_size", text="Size")
- col.prop(tool_settings, "sculpt_paint_use_unified_strength", text="Strength")
-
- col = split.column()
-
- col.label(text="Lock:")
- row = col.row(align=True)
+ layout.label(text="Lock:")
+ row = layout.row(align=True)
row.prop(sculpt, "lock_x", text="X", toggle=True)
row.prop(sculpt, "lock_y", text="Y", toggle=True)
row.prop(sculpt, "lock_z", text="Z", toggle=True)
+ layout.prop(sculpt, "use_threaded", text="Threaded Sculpt")
+ layout.prop(sculpt, "show_low_resolution")
+ layout.prop(sculpt, "show_brush")
+ layout.prop(sculpt, "use_deform_only")
+
+ layout.label(text="Unified Settings:")
+ layout.prop(tool_settings, "sculpt_paint_use_unified_size", text="Size")
+ layout.prop(tool_settings, "sculpt_paint_use_unified_strength", text="Strength")
+
class VIEW3D_PT_sculpt_symmetry(PaintPanel, bpy.types.Panel):
bl_label = "Symmetry"
@@ -973,31 +971,24 @@ class VIEW3D_PT_sculpt_symmetry(PaintPanel, bpy.types.Panel):
return (context.sculpt_object and context.tool_settings.sculpt)
def draw(self, context):
-
layout = self.layout
sculpt = context.tool_settings.sculpt
settings = __class__.paint_settings(context)
- brush = settings.brush
split = layout.split()
col = split.column()
-
col.label(text="Mirror:")
col.prop(sculpt, "use_symmetry_x", text="X")
col.prop(sculpt, "use_symmetry_y", text="Y")
col.prop(sculpt, "use_symmetry_z", text="Z")
- col = split.column()
-
- col.prop(sculpt, "radial_symmetry", text="Radial")
-
- col = layout.column()
+ split.prop(sculpt, "radial_symmetry", text="Radial")
- col.separator()
+ layout.separator()
- col.prop(sculpt, "use_symmetry_feather", text="Feather")
+ layout.prop(sculpt, "use_symmetry_feather", text="Feather")
class VIEW3D_PT_tools_brush_appearance(PaintPanel, bpy.types.Panel):
@@ -1006,20 +997,19 @@ 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.texture_paint_object and context.tool_settings.image_paint)
+ 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)
def draw(self, context):
layout = self.layout
- sculpt = context.tool_settings.sculpt
settings = __class__.paint_settings(context)
brush = settings.brush
col = layout.column()
if context.sculpt_object and context.tool_settings.sculpt:
- #if brush.sculpt_tool in ('DRAW', 'INFLATE', 'CLAY', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN', 'FILL', 'SCRAPE', 'CLAY_TUBES'):
- if brush.sculpt_tool in ('DRAW', 'INFLATE', 'CLAY', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN', 'FILL', 'SCRAPE'):
+ #if brush.sculpt_tool in {'DRAW', 'INFLATE', 'CLAY', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN', 'FILL', 'SCRAPE', 'CLAY_TUBES'}:
+ if brush.sculpt_tool in {'DRAW', 'INFLATE', 'CLAY', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN', 'FILL', 'SCRAPE'}:
col.prop(brush, "cursor_color_add", text="Add Color")
col.prop(brush, "cursor_color_subtract", text="Subtract Color")
else:
@@ -1124,13 +1114,13 @@ class VIEW3D_PT_tools_vertexpaint(View3DPanel, bpy.types.Panel):
class VIEW3D_PT_tools_projectpaint(View3DPanel, bpy.types.Panel):
- bl_context = "texturepaint"
+ bl_context = "imagepaint"
bl_label = "Project Paint"
@classmethod
def poll(cls, context):
brush = context.tool_settings.image_paint.brush
- return (brush and brush.imagepaint_tool != 'SOFTEN')
+ return (brush and brush.image_tool != 'SOFTEN')
def draw_header(self, context):
ipaint = context.tool_settings.image_paint
@@ -1140,6 +1130,8 @@ class VIEW3D_PT_tools_projectpaint(View3DPanel, bpy.types.Panel):
def draw(self, context):
layout = self.layout
+ ob = context.active_object
+ mesh = ob.data
ipaint = context.tool_settings.image_paint
settings = context.tool_settings.image_paint
use_projection = ipaint.use_projection
@@ -1167,16 +1159,16 @@ class VIEW3D_PT_tools_projectpaint(View3DPanel, bpy.types.Panel):
row2 = row.row(align=False)
row2.active = (use_projection and ipaint.use_stencil_layer)
- row2.menu("VIEW3D_MT_tools_projectpaint_stencil", text=context.active_object.data.uv_texture_stencil.name)
+ row2.menu("VIEW3D_MT_tools_projectpaint_stencil", text=mesh.uv_texture_stencil.name)
row2.prop(ipaint, "invert_stencil", text="", icon='IMAGE_ALPHA')
col = layout.column()
sub = col.column()
row = sub.row()
- row.active = (settings.brush.imagepaint_tool == 'CLONE')
+ row.active = (settings.brush.image_tool == 'CLONE')
row.prop(ipaint, "use_clone_layer", text="Layer")
- row.menu("VIEW3D_MT_tools_projectpaint_clone", text=context.active_object.data.uv_texture_clone.name)
+ row.menu("VIEW3D_MT_tools_projectpaint_clone", text=mesh.uv_texture_clone.name)
sub = col.column()
sub.prop(ipaint, "seam_bleed")
@@ -1200,14 +1192,14 @@ class VIEW3D_PT_imagepaint_options(PaintPanel):
@classmethod
def poll(cls, context):
- return (context.texture_paint_object and context.tool_settings.image_paint)
+ return (context.image_paint_object and context.tool_settings.image_paint)
def draw(self, context):
layout = self.layout
- col = layout.column()
-
tool_settings = context.tool_settings
+
+ col = layout.column()
col.label(text="Unified Settings:")
col.prop(tool_settings, "sculpt_paint_use_unified_size", text="Size")
col.prop(tool_settings, "sculpt_paint_use_unified_strength", text="Strength")
@@ -1300,13 +1292,5 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, bpy.types.Panel):
sub.active = pe.use_fade_time
sub.prop(pe, "fade_frames", slider=True)
-
-def register():
+if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-if __name__ == "__main__":
- register()
diff --git a/release/scripts/keyingsets/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index cff4aecac54..8cb63ea48cf 100644
--- a/release/scripts/keyingsets/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -23,10 +23,15 @@ Built-In Keying Sets
None of these Keying Sets should be removed, as these
are needed by various parts of Blender in order for them
to work correctly.
+
+Beware also about changing the order that these are defined
+here, since this can result in old files referring to the
+wrong Keying Set as the active one, potentially resulting
+in lost (i.e. unkeyed) animation.
"""
import bpy
-from keyingsets_utils import *
+import keyingsets_utils
###############################
# Built-In KeyingSets
@@ -37,13 +42,13 @@ class BUILTIN_KSI_Location(bpy.types.KeyingSetInfo):
bl_label = "Location"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator - use callback for location
- generate = RKS_GEN_location
+ generate = keyingsets_utils.RKS_GEN_location
# Rotation
@@ -51,13 +56,13 @@ class BUILTIN_KSI_Rotation(bpy.types.KeyingSetInfo):
bl_label = "Rotation"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator - use callback for location
- generate = RKS_GEN_rotation
+ generate = keyingsets_utils.RKS_GEN_rotation
# Scale
@@ -65,13 +70,13 @@ class BUILTIN_KSI_Scaling(bpy.types.KeyingSetInfo):
bl_label = "Scaling"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator - use callback for location
- generate = RKS_GEN_scaling
+ generate = keyingsets_utils.RKS_GEN_scaling
# ------------
@@ -81,17 +86,17 @@ class BUILTIN_KSI_LocRot(bpy.types.KeyingSetInfo):
bl_label = "LocRot"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator
def generate(self, context, ks, data):
# location
- RKS_GEN_location(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_location(self, context, ks, data)
# rotation
- RKS_GEN_rotation(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
# LocScale
@@ -99,17 +104,17 @@ class BUILTIN_KSI_LocScale(bpy.types.KeyingSetInfo):
bl_label = "LocScale"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator
def generate(self, context, ks, data):
# location
- RKS_GEN_location(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_location(self, context, ks, data)
# scale
- RKS_GEN_scaling(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
# LocRotScale
@@ -117,19 +122,19 @@ class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo):
bl_label = "LocRotScale"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator
def generate(self, context, ks, data):
# location
- RKS_GEN_location(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_location(self, context, ks, data)
# rotation
- RKS_GEN_rotation(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
# scale
- RKS_GEN_scaling(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
# RotScale
@@ -137,17 +142,17 @@ class BUILTIN_KSI_RotScale(bpy.types.KeyingSetInfo):
bl_label = "RotScale"
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator
def generate(self, context, ks, data):
# rotation
- RKS_GEN_rotation(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
# scaling
- RKS_GEN_scaling(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
# ------------
@@ -159,13 +164,13 @@ class BUILTIN_KSI_VisualLoc(bpy.types.KeyingSetInfo):
bl_options = {'INSERTKEY_VISUAL'}
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator - use callback for location
- generate = RKS_GEN_location
+ generate = keyingsets_utils.RKS_GEN_location
# Rotation
@@ -175,13 +180,13 @@ class BUILTIN_KSI_VisualRot(bpy.types.KeyingSetInfo):
bl_options = {'INSERTKEY_VISUAL'}
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator - use callback for rotation
- generate = RKS_GEN_rotation
+ generate = keyingsets_utils.RKS_GEN_rotation
# VisualLocRot
@@ -191,17 +196,17 @@ class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo):
bl_options = {'INSERTKEY_VISUAL'}
# poll - use predefined callback for selected bones/objects
- poll = RKS_POLL_selected_items
+ poll = keyingsets_utils.RKS_POLL_selected_items
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator
def generate(self, context, ks, data):
# location
- RKS_GEN_location(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_location(self, context, ks, data)
# rotation
- RKS_GEN_rotation(self, context, ks, data)
+ keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
# ------------
@@ -210,16 +215,20 @@ class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo):
class BUILTIN_KSI_Available(bpy.types.KeyingSetInfo):
bl_label = "Available"
- # poll - use predefined callback for selected objects
- # TODO: this should really check whether the selected object (or datablock)
- # has any animation data defined yet
- poll = RKS_POLL_selected_objects
+ # poll - selected objects or selected object with animation data
+ def poll(ksi, context):
+ ob = context.active_object
+ if ob:
+ # TODO: this fails if one animation-less object is active, but many others are selected
+ return ob.animation_data and ob.animation_data.action
+ else:
+ return bool(context.selected_objects)
# iterator - use callback for selected bones/objects
- iterator = RKS_ITER_selected_item
+ iterator = keyingsets_utils.RKS_ITER_selected_item
# generator - use callback for doing this
- generate = RKS_GEN_available
+ generate = keyingsets_utils.RKS_GEN_available
###############################
@@ -278,7 +287,7 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
path = id_path + prop
else:
# standard transforms/properties
- path = path_add_property(id_path, prop)
+ path = keyingsets_utils.path_add_property(id_path, prop)
# add Keying Set entry for this...
if use_groups:
@@ -352,6 +361,93 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo):
# for now, just add all of 'em
ksi.addProp(ks, bone, '["%s"]' % (prop))
+###############################
+
+
+# Delta Location
+class BUILTIN_KSI_DeltaLocation(bpy.types.KeyingSetInfo):
+ bl_label = "Delta Location"
+
+ # poll - selected objects only (and only if active object in object mode)
+ poll = keyingsets_utils.RKS_POLL_selected_objects
+
+ # iterator - selected objects only
+ iterator = keyingsets_utils.RKS_ITER_selected_objects
+
+ # generator - delta location channels only
+ def generate(ksi, context, ks, data):
+ # get id-block and path info
+ id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
+
+ # add the property name to the base path
+ path = keyingsets_utils.path_add_property(base_path, "delta_location")
+
+ # add Keying Set entry for this...
+ if grouping:
+ ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
+ else:
+ ks.paths.add(id_block, path)
+
+
+# Delta Rotation
+class BUILTIN_KSI_DeltaRotation(bpy.types.KeyingSetInfo):
+ bl_label = "Delta Rotation"
+
+ # poll - selected objects only (and only if active object in object mode)
+ poll = keyingsets_utils.RKS_POLL_selected_objects
+
+ # iterator - selected objects only
+ iterator = keyingsets_utils.RKS_ITER_selected_objects
+
+ # generator - delta location channels only
+ def generate(ksi, context, ks, data):
+ # get id-block and path info
+ id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
+
+ # 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")
+ elif data.rotation_mode == 'AXIS_ANGLE':
+ # XXX: for now, this is not available yet
+ #path = path_add_property(base_path, "delta_rotation_axis_angle")
+ return
+ else:
+ path = keyingsets_utils.path_add_property(base_path, "delta_rotation_euler")
+
+ # add Keying Set entry for this...
+ if grouping:
+ ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
+ else:
+ ks.paths.add(id_block, path)
+
+
+# Delta Scale
+class BUILTIN_KSI_DeltaScale(bpy.types.KeyingSetInfo):
+ bl_label = "Delta Scale"
+
+ # poll - selected objects only (and only if active object in object mode)
+ poll = keyingsets_utils.RKS_POLL_selected_objects
+
+ # iterator - selected objects only
+ iterator = keyingsets_utils.RKS_ITER_selected_objects
+
+ # generator - delta location channels only
+ def generate(ksi, context, ks, data):
+ # get id-block and path info
+ id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
+
+ # add the property name to the base path
+ path = keyingsets_utils.path_add_property(base_path, "delta_scale")
+
+ # add Keying Set entry for this...
+ if grouping:
+ ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
+ else:
+ ks.paths.add(id_block, path)
+
+###############################
+
def register():
bpy.utils.register_module(__name__)
@@ -363,5 +459,3 @@ def unregister():
if __name__ == "__main__":
register()
-
-###############################
diff --git a/release/scripts/templates/addon_add_object.py b/release/scripts/templates/addon_add_object.py
index f4f1185e282..67e033271f4 100644
--- a/release/scripts/templates/addon_add_object.py
+++ b/release/scripts/templates/addon_add_object.py
@@ -31,8 +31,10 @@ def add_object(self, context):
edges = []
faces = [[0, 1, 2, 3]]
- mesh_data = bpy.data.meshes.new(name='New Object Mesh')
- mesh_data.from_pydata(verts, edges, faces)
+ mesh = bpy.data.meshes.new(name='New Object Mesh')
+ mesh.from_pydata(verts, edges, faces)
+ # useful for development when the mesh may be invalid.
+ # mesh.validate(verbose=True)
add_object_data(context, mesh_data, operator=self)
@@ -55,7 +57,7 @@ class OBJECT_OT_add_object(bpy.types.Operator, AddObjectHelper):
return {'FINISHED'}
-#### REGISTER ####
+# Registration
def add_object_button(self, context):
self.layout.operator(
diff --git a/release/scripts/templates/background_job.py b/release/scripts/templates/background_job.py
index 0337f8f4922..11b51e5a9b5 100644
--- a/release/scripts/templates/background_job.py
+++ b/release/scripts/templates/background_job.py
@@ -1,12 +1,18 @@
-# This script is an example of how you can run blender from the command line (in background mode with no interface)
-# to automate tasks, in this example it creates a text object, camera and light, then renders and/or saves it.
-# This example also shows how you can parse command line options to python scripts.
+# This script is an example of how you can run blender from the command line
+# (in background mode with no interface) to automate tasks, in this example it
+# creates a text object, camera and light, then renders and/or saves it.
+# This example also shows how you can parse command line options to scripts.
#
# Example usage for this test.
-# blender --background --factory-startup --python $HOME/background_job.py -- --text="Hello World" --render="/tmp/hello" --save="/tmp/hello.blend"
+# blender --background --factory-startup --python $HOME/background_job.py -- \
+# --text="Hello World" \
+# --render="/tmp/hello" \
+# --save="/tmp/hello.blend"
#
# Notice:
-# '--factory-startup' is used to avoid the user default settings from interfearing with automated scene generation.
+# '--factory-startup' is used to avoid the user default settings from
+# interfearing with automated scene generation.
+#
# '--' causes blender to ignore all following arguments so python can use them.
#
# See blender --help for details.
@@ -14,7 +20,7 @@
import bpy
-def example_function(body_text, save_path, render_path):
+def example_function(text, save_path, render_path):
scene = bpy.context.scene
@@ -27,15 +33,15 @@ def example_function(body_text, save_path, render_path):
# Text Object
txt_ob = bpy.data.objects.new(name="MyText", object_data=txt_data)
- scene.objects.link(txt_ob) # add the data to the scene as an object
- txt_data.body = body_text # set the body text to the command line arg given
- txt_data.align = 'CENTER' # center text
+ scene.objects.link(txt_ob) # add the data to the scene as an object
+ txt_data.body = text # the body text to the command line arg given
+ txt_data.align = 'CENTER' # center text
# Camera
- cam_data = bpy.data.cameras.new("MyCam") # create new camera data
+ cam_data = bpy.data.cameras.new("MyCam")
cam_ob = bpy.data.objects.new(name="MyCam", object_data=cam_data)
- scene.objects.link(cam_ob) # add the camera data to the scene (creating a new object)
- scene.camera = cam_ob # set the active camera
+ scene.objects.link(cam_ob) # instance the camera object in the scene
+ scene.camera = cam_ob # set the active camera
cam_ob.location = 0.0, 0.0, 10.0
# Lamp
@@ -65,14 +71,12 @@ def example_function(body_text, save_path, render_path):
bpy.ops.render.render(write_still=True)
-import sys # to get command line args
-import optparse # to parse options for us and print a nice help message
-
-
def main():
+ import sys # to get command line args
+ import argparse # to parse options for us and print a nice help message
- # get the args passed to blender after "--", all of which are ignored by blender specifically
- # so python may receive its own arguments
+ # get the args passed to blender after "--", all of which are ignored by
+ # blender so scripts may receive their own arguments
argv = sys.argv
if "--" not in argv:
@@ -81,31 +85,35 @@ def main():
argv = argv[argv.index("--") + 1:] # get all args after "--"
# When --help or no args are given, print this help
- usage_text = "Run blender in background mode with this script:"
- usage_text += " blender --background --python " + __file__ + " -- [options]"
+ usage_text = \
+ "Run blender in background mode with this script:"
+ " blender --background --python " + __file__ + " -- [options]"
- parser = optparse.OptionParser(usage=usage_text)
+ parser = argparse.ArgumentParser(description=usage_text)
- # Example background utility, add some text and renders or saves it (with options)
+ # Example utility, add some text and renders or saves it (with options)
# Possible types are: string, int, long, choice, float and complex.
- parser.add_option("-t", "--text", dest="body_text", help="This text will be used to render an image", type="string")
+ parser.add_argument("-t", "--text", dest="text", type=str, required=True,
+ help="This text will be used to render an image")
- parser.add_option("-s", "--save", dest="save_path", help="Save the generated file to the specified path", metavar='FILE')
- parser.add_option("-r", "--render", dest="render_path", help="Render an image to the specified path", metavar='FILE')
+ parser.add_argument("-s", "--save", dest="save_path", metavar='FILE',
+ help="Save the generated file to the specified path")
+ parser.add_argument("-r", "--render", dest="render_path", metavar='FILE',
+ help="Render an image to the specified path")
- options, args = parser.parse_args(argv) # In this example we wont use the args
+ args = parser.parse_args(argv) # In this example we wont use the args
if not argv:
parser.print_help()
return
- if not options.body_text:
+ if not args.text:
print("Error: --text=\"some string\" argument not given, aborting.")
parser.print_help()
return
# Run the example function
- example_function(options.body_text, options.save_path, options.render_path)
+ example_function(args.text, args.save_path, args.render_path)
print("batch job finished, exiting")
diff --git a/release/scripts/templates/builtin_keyingset.py b/release/scripts/templates/builtin_keyingset.py
index 0b808e9cd26..19f92dc75e7 100644
--- a/release/scripts/templates/builtin_keyingset.py
+++ b/release/scripts/templates/builtin_keyingset.py
@@ -1,5 +1,4 @@
import bpy
-from keyingsets_utils import *
class BUILTIN_KSI_hello(bpy.types.KeyingSetInfo):
@@ -7,7 +6,7 @@ class BUILTIN_KSI_hello(bpy.types.KeyingSetInfo):
# poll - test for whether Keying Set can be used at all
def poll(ksi, context):
- return (context.active_object) or (context.selected_objects)
+ return context.active_object or context.selected_objects
# iterator - go over all relevant data, calling generate()
def iterator(ksi, context, ks):
diff --git a/release/scripts/templates/operator_export.py b/release/scripts/templates/operator_export.py
index 5390d32aeff..1b1c90c8a21 100644
--- a/release/scripts/templates/operator_export.py
+++ b/release/scripts/templates/operator_export.py
@@ -13,12 +13,11 @@ 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.props import *
+from bpy.props import StringProperty, BoolProperty, EnumProperty
class ExportSomeData(bpy.types.Operator, ExportHelper):
- '''This appiers in the tooltip of the operator and in the generated docs.'''
+ '''This appears in the tooltip of the operator and in the generated docs.'''
bl_idname = "export.some_data" # this is important since its how bpy.ops.export.some_data is constructed
bl_label = "Export Some Data"
@@ -31,7 +30,9 @@ class ExportSomeData(bpy.types.Operator, ExportHelper):
# to the class instance from the operator settings before calling.
use_setting = BoolProperty(name="Example Boolean", description="Example Tooltip", default=True)
- type = bpy.props.EnumProperty(items=(('OPT_A', "First Option", "Description one"), ('OPT_B', "Second Option", "Description two.")),
+ type = EnumProperty(items=(('OPT_A', "First Option", "Description one"),
+ ('OPT_B', "Second Option", "Description two."),
+ ),
name="Example Enum",
description="Choose between two items",
default='OPT_A')
diff --git a/release/scripts/templates/operator_mesh_add.py b/release/scripts/templates/operator_mesh_add.py
index 77d172b3068..65b08eebb4e 100644
--- a/release/scripts/templates/operator_mesh_add.py
+++ b/release/scripts/templates/operator_mesh_add.py
@@ -34,7 +34,7 @@ def add_box(width, height, depth):
return vertices, faces
-from bpy.props import *
+from bpy.props import FloatProperty, BoolProperty, FloatVectorProperty
class AddBox(bpy.types.Operator):
diff --git a/release/scripts/templates/operator_modal.py b/release/scripts/templates/operator_modal.py
index 6eb3843d6a6..78dbd4c6b43 100644
--- a/release/scripts/templates/operator_modal.py
+++ b/release/scripts/templates/operator_modal.py
@@ -1,5 +1,5 @@
import bpy
-from bpy.props import *
+from bpy.props import IntProperty, FloatProperty
class ModalOperator(bpy.types.Operator):
diff --git a/release/scripts/templates/operator_modal_timer.py b/release/scripts/templates/operator_modal_timer.py
new file mode 100644
index 00000000000..d2267191cf5
--- /dev/null
+++ b/release/scripts/templates/operator_modal_timer.py
@@ -0,0 +1,45 @@
+import bpy
+
+
+class ModalTimerOperator(bpy.types.Operator):
+ '''Operator which runs its self from a timer.'''
+ bl_idname = "wm.modal_timer_operator"
+ bl_label = "Modal Timer Operator"
+
+ _timer = None
+
+ def modal(self, context, event):
+ if event.type == 'ESC':
+ return self.cancel()
+
+ if event.type == 'TIMER':
+ # change theme color, silly!
+ color = context.user_preferences.themes[0].view_3d.back
+ color.s = 1.0
+ color.h += 0.01
+
+ return {'PASS_THROUGH'}
+
+ def execute(self, context):
+ context.window_manager.modal_handler_add(self)
+ self._timer = context.window_manager.event_timer_add(0.1, context.window)
+ return {'RUNNING_MODAL'}
+
+ def cancel(self, context):
+ context.window_manager.event_timer_remove(self._timer)
+ return {'CANCELLED'}
+
+
+def register():
+ bpy.utils.register_class(ModalTimerOperator)
+
+
+def unregister():
+ bpy.utils.unregister_class(ModalTimerOperator)
+
+
+if __name__ == "__main__":
+ register()
+
+ # test call
+ bpy.ops.wm.modal_timer_operator()
diff --git a/release/scripts/templates/panel_simple.py b/release/scripts/templates/panel_simple.py
index ddbd4eb7aa4..e5bf70cb654 100644
--- a/release/scripts/templates/panel_simple.py
+++ b/release/scripts/templates/panel_simple.py
@@ -26,7 +26,7 @@ def register():
def unregister():
- bpy.utils.unregister_class(OBJECT_PT_hello)
+ bpy.utils.unregister_class(OBJECT_PT_hello)
if __name__ == "__main__":