Merge with trunk r38042

This commit is contained in:
2011-07-02 18:15:55 +00:00
62 changed files with 2714 additions and 2280 deletions

View File

@@ -60,7 +60,7 @@ class EditExternally(bpy.types.Operator):
filepath = bpy.path.abspath(self.filepath)
if not os.path.exists(filepath):
self.report({'ERROR'}, "Image path %r not found." % filepath)
self.report({'ERROR'}, "Image path %r not found, image may be packed or unsaved." % filepath)
return {'CANCELLED'}
cmd = self._editor_guess(context) + [filepath]

View File

@@ -81,8 +81,8 @@ class MeshMirrorUV(bpy.types.Operator):
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH')
obj = context.active_object
return (obj and obj.type == 'MESH' and obj.data.uv_textures.active)
def execute(self, context):
DIR = (self.direction == 'NEGATIVE')
@@ -120,12 +120,7 @@ class MeshMirrorUV(bpy.types.Operator):
if j is not None:
vmap[i] = j
active_uv_layer = None
for lay in mesh.uv_textures:
if lay.active:
active_uv_layer = lay.data
break
active_uv_layer = mesh.uv_textures.active.data
fuvs = [(uv.uv1, uv.uv2, uv.uv3, uv.uv4) for uv in active_uv_layer]
fuvs_cpy = [(uv[0].copy(), uv[1].copy(), uv[2].copy(), uv[3].copy()) for uv in fuvs]

View File

@@ -132,7 +132,7 @@ class QuickExplode(bpy.types.Operator):
fake_context = bpy.context.copy()
obj_act = context.active_object
if obj_act.type != 'MESH':
if obj_act is None or obj_act.type != 'MESH':
self.report({'ERROR'}, "Active object is not a mesh")
return {'CANCELLED'}

View File

@@ -746,13 +746,15 @@ def packIslands(islandList):
uv.y= (uv.y+yoffset) * yfactor
def VectoQuat(vec):
vec = vec.normalized()
if abs(vec.x) > 0.5:
return vec.to_track_quat('Z', 'X')
else:
return vec.to_track_quat('Z', 'Y')
a3 = vec.normalized()
up = Vector((0.0, 0.0, 1.0))
if abs(a3.dot(up)) == 1.0:
up = Vector((0.0, 1.0, 0.0))
a1 = a3.cross(up).normalized()
a2 = a3.cross(a1)
return Matrix((a1, a2, a3)).to_quaternion()
class thickface(object):
@@ -791,7 +793,11 @@ def main_consts():
global ob
ob = None
def main(context, island_margin, projection_limit):
def main(context,
island_margin,
projection_limit,
user_area_weight,
):
global USER_FILL_HOLES
global USER_FILL_HOLES_QUALITY
global USER_STRETCH_ASPECT
@@ -844,7 +850,6 @@ def main(context, island_margin, projection_limit):
USER_FILL_HOLES = (0)
USER_FILL_HOLES_QUALITY = (50) # Only for hole filling.
USER_VIEW_INIT = (0) # Only for hole filling.
USER_AREA_WEIGHT = (1) # Only for hole filling.
# Reuse variable
if len(obList) == 1:
@@ -970,12 +975,15 @@ def main(context, island_margin, projection_limit):
# Add the average of all these faces normals as a projectionVec
averageVec = Vector((0.0, 0.0, 0.0))
if USER_AREA_WEIGHT:
for fprop in newProjectMeshFaces:
averageVec += (fprop.no * fprop.area)
else:
if user_area_weight == 0.0:
for fprop in newProjectMeshFaces:
averageVec += fprop.no
elif user_area_weight == 1.0:
for fprop in newProjectMeshFaces:
averageVec += fprop.no * fprop.area
else:
for fprop in newProjectMeshFaces:
averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight))
if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
projectVecs.append(averageVec.normalized())
@@ -1062,7 +1070,7 @@ def main(context, island_margin, projection_limit):
f_uv = f.uv
for j, v in enumerate(f.v):
# XXX - note, between mathutils in 2.4 and 2.5 the order changed.
f_uv[j][:] = (v.co * MatQuat)[:2]
f_uv[j][:] = (v.co * MatQuat).xy
if USER_SHARE_SPACE:
@@ -1098,12 +1106,8 @@ def main(context, island_margin, projection_limit):
"""
pup_block = [\
'Projection',\
* ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, ''),\
('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\
('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\
'',\
'',\
'',\
'UV Layout',\
('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\
@@ -1125,11 +1129,15 @@ class SmartProject(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'}
angle_limit = FloatProperty(name="Angle Limit",
description="lower for more projection groups, higher for less distortion.",
description="lower for more projection groups, higher for less distortion",
default=66.0, min=1.0, max=89.0)
island_margin = FloatProperty(name="Island Margin",
description="Margin to reduce bleed from adjacent islands.",
description="Margin to reduce bleed from adjacent islands",
default=0.0, min=0.0, max=1.0)
user_area_weight = FloatProperty(name="Area Weight",
description="Weight projections vector by faces with larger areas",
default=0.0, min=0.0, max=1.0)
@classmethod
@@ -1137,7 +1145,11 @@ class SmartProject(bpy.types.Operator):
return context.active_object != None
def execute(self, context):
main(context, self.island_margin, self.angle_limit)
main(context,
self.island_margin,
self.angle_limit,
self.user_area_weight,
)
return {'FINISHED'}
def invoke(self, context, event):

View File

@@ -19,7 +19,9 @@
# <pep8 compliant>
import bpy
from bpy.props import StringProperty, BoolProperty, IntProperty, FloatProperty
from bpy.props import StringProperty, BoolProperty, IntProperty, \
FloatProperty, EnumProperty
from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear
@@ -457,6 +459,66 @@ doc_id = StringProperty(name="Doc ID",
doc_new = StringProperty(name="Edit Description",
description="", maxlen=1024, default="")
data_path_iter = StringProperty(
description="The data path relative to the context, must point to an iterable.")
data_path_item = StringProperty(
description="The data path from each iterable to the value (int or float)")
class WM_OT_context_collection_boolean_set(bpy.types.Operator):
'''Set boolean values for a collection of items'''
bl_idname = "wm.context_collection_boolean_set"
bl_label = "Context Collection Boolean Set"
bl_options = {'UNDO', 'REGISTER', 'INTERNAL'}
data_path_iter = data_path_iter
data_path_item = data_path_item
type = EnumProperty(items=(
('TOGGLE', "Toggle", ""),
('ENABLE', "Enable", ""),
('DISABLE', "Disable", ""),
),
name="Type")
def execute(self, context):
data_path_iter = self.data_path_iter
data_path_item = self.data_path_item
items = list(getattr(context, data_path_iter))
items_ok = []
is_set = False
for item in items:
try:
value_orig = eval("item." + data_path_item)
except:
continue
if value_orig == True:
is_set = True
elif value_orig == False:
pass
else:
self.report({'WARNING'}, "Non boolean value found: %s[ ].%s" %
(data_path_iter, data_path_item))
return {'CANCELLED'}
items_ok.append(item)
if self.type == 'ENABLE':
is_set = True
elif self.type == 'DISABLE':
is_set = False
else:
is_set = not is_set
exec_str = "item.%s = %s" % (data_path_item, is_set)
for item in items_ok:
exec(exec_str)
return {'FINISHED'}
class WM_OT_context_modal_mouse(bpy.types.Operator):
'''Adjust arbitrary values with mouse input'''
@@ -464,8 +526,9 @@ class WM_OT_context_modal_mouse(bpy.types.Operator):
bl_label = "Context Modal Mouse"
bl_options = {'GRAB_POINTER', 'BLOCKING', 'INTERNAL'}
data_path_iter = StringProperty(description="The data path relative to the context, must point to an iterable.")
data_path_item = StringProperty(description="The data path from each iterable to the value (int or float)")
data_path_iter = data_path_iter
data_path_item = data_path_item
input_scale = FloatProperty(default=0.01, description="Scale the mouse movement by this value before applying the delta")
invert = BoolProperty(default=False, description="Invert the mouse input")
initial_x = IntProperty(options={'HIDDEN'})

View File

@@ -878,7 +878,7 @@ class PARTICLE_PT_render(ParticleButtonsPanel, bpy.types.Panel):
col.prop(part, "billboard_tilt_random", text="Random", slider=True)
col = row.column()
col.prop(part, "billboard_offset")
row = layout.row()
col = row.column()
col.prop(part, "billboard_size", text="Scale")

View File

@@ -616,10 +616,9 @@ class IMAGE_PT_view_properties(bpy.types.Panel):
split = layout.split()
col = split.column()
col.prop(uvedit, "show_faces")
col.prop(uvedit, "show_smooth_edges", text="Smooth")
col.prop(uvedit, "show_modified_edges", text="Modified")
#col.prop(uvedit, "show_edges")
#col.prop(uvedit, "show_faces")
col = split.column()
col.prop(uvedit, "show_stretch", text="Stretch")

View File

@@ -876,6 +876,19 @@ class USERPREF_PT_addons(bpy.types.Panel):
def module_get(mod_name):
return USERPREF_PT_addons._addons_fake_modules[mod_name]
@staticmethod
def is_user_addon(mod, user_addon_paths):
if not user_addon_paths:
user_script_path = bpy.utils.user_script_path()
if user_script_path is not None:
user_addon_paths.append(os.path.join(user_script_path(), "addons"))
user_addon_paths.append(os.path.join(bpy.utils.resource_path('USER'), "scripts", "addons"))
for path in user_addon_paths:
if bpy.path.is_subdir(mod.__file__, path):
return True
return False
def draw(self, context):
layout = self.layout
@@ -900,6 +913,9 @@ class USERPREF_PT_addons(bpy.types.Panel):
search = context.window_manager.addon_search.lower()
support = context.window_manager.addon_support
# initialized on demand
user_addon_paths = []
for mod, info in addons:
module_name = mod.__name__
@@ -969,20 +985,24 @@ class USERPREF_PT_addons(bpy.types.Panel):
split = colsub.row().split(percentage=0.15)
split.label(text="Warning:")
split.label(text=' ' + info["warning"], icon='ERROR')
if info["wiki_url"] or info["tracker_url"]:
user_addon = __class__.is_user_addon(mod, user_addon_paths)
tot_row = bool(info["wiki_url"]) + bool(info["tracker_url"]) + bool(user_addon)
if tot_row:
split = colsub.row().split(percentage=0.15)
split.label(text="Internet:")
if info["wiki_url"]:
split.operator("wm.url_open", text="Link to the Wiki", icon='HELP').url = info["wiki_url"]
if info["tracker_url"]:
split.operator("wm.url_open", text="Report a Bug", icon='URL').url = info["tracker_url"]
if user_addon:
split.operator("wm.addon_remove", text="Remove", icon='CANCEL').module = mod.__name__
if info["wiki_url"] and info["tracker_url"]:
split.separator()
else:
split.separator()
for i in range(4 - tot_row):
split.separator()
# Append missing scripts
# First collect scripts that are used but have no script file.
module_names = {mod.__name__ for mod, info in addons}
@@ -1186,6 +1206,54 @@ class WM_OT_addon_install(bpy.types.Operator):
return {'RUNNING_MODAL'}
class WM_OT_addon_remove(bpy.types.Operator):
"Disable an addon"
bl_idname = "wm.addon_remove"
bl_label = "Remove Add-On"
module = StringProperty(name="Module", description="Module name of the addon to remove")
@staticmethod
def path_from_addon(module):
for mod in addon_utils.modules(USERPREF_PT_addons._addons_fake_modules):
if mod.__name__ == module:
filepath = mod.__file__
if os.path.exists(filepath):
if os.path.splitext(os.path.basename(filepath))[0] == "__init__":
return os.path.dirname(filepath), True
else:
return filepath, False
return None, False
def execute(self, context):
path, isdir = __class__.path_from_addon(self.module)
if path is None:
self.report('WARNING', "Addon path %r could not be found" % path)
return {'CANCELLED'}
# incase its enabled
addon_utils.disable(self.module)
import shutil
if isdir:
shutil.rmtree(path)
else:
os.remove(path)
context.area.tag_redraw()
return {'FINISHED'}
# lame confirmation check
def draw(self, context):
self.layout.label(text="Remove Addon: %r?" % self.module)
path, isdir = __class__.path_from_addon(self.module)
self.layout.label(text="Path: %r" % path)
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=600)
class WM_OT_addon_expand(bpy.types.Operator):
"Display more information on this add-on"
bl_idname = "wm.addon_expand"

View File

@@ -1252,7 +1252,7 @@ class VIEW3D_MT_pose(bpy.types.Menu):
layout.separator()
layout.menu("VIEW3D_MT_pose_showhide")
layout.operator_menu_enum("pose.flags_set", 'mode', text="Bone Settings")
layout.menu("VIEW3D_MT_bone_options_toggle", text="Bone Settings")
class VIEW3D_MT_pose_transform(bpy.types.Menu):
@@ -1373,6 +1373,49 @@ class VIEW3D_MT_pose_apply(bpy.types.Menu):
layout.operator("pose.visual_transform_apply")
class BoneOptions:
def draw(self, context):
layout = self.layout
options = [
"show_wire",
"use_deform",
"use_envelope_multiply",
"use_inherit_rotation",
"use_inherit_scale",
]
if context.mode == 'EDIT_ARMATURE':
bone_props = bpy.types.EditBone.bl_rna.properties
data_path_iter = "selected_bones"
opt_suffix = ""
options.append("lock")
else: # posemode
bone_props = bpy.types.Bone.bl_rna.properties
data_path_iter = "selected_pose_bones"
opt_suffix = "bone."
for opt in options:
props = layout.operator("wm.context_collection_boolean_set", text=bone_props[opt].name)
props.data_path_iter = data_path_iter
props.data_path_item = opt_suffix + opt
props.type = self.type
class VIEW3D_MT_bone_options_toggle(bpy.types.Menu, BoneOptions):
bl_label = "Toggle Bone Options"
type = 'TOGGLE'
class VIEW3D_MT_bone_options_enable(bpy.types.Menu, BoneOptions):
bl_label = "Enable Bone Options"
type = 'ENABLE'
class VIEW3D_MT_bone_options_disable(bpy.types.Menu, BoneOptions):
bl_label = "Disable Bone Options"
type = 'DISABLE'
# ********** Edit Menus, suffix from ob.type **********
@@ -1966,7 +2009,7 @@ class VIEW3D_MT_edit_armature(bpy.types.Menu):
layout.separator()
layout.operator_menu_enum("armature.flags_set", "mode", text="Bone Settings")
layout.menu("VIEW3D_MT_bone_options_toggle", text="Bone Settings")
class VIEW3D_MT_armature_specials(bpy.types.Menu):