diff options
author | Joerg Mueller <nexyon@gmail.com> | 2011-07-22 01:11:58 +0400 |
---|---|---|
committer | Joerg Mueller <nexyon@gmail.com> | 2011-07-22 01:11:58 +0400 |
commit | 4532bd731d5edbe348d4df810856f6bdfdea705c (patch) | |
tree | 778fdcd594b5f384eacf5cd82f50afc10bbed513 /release | |
parent | cf34f7509f4ea8c3f0c92045933f089c72de5313 (diff) | |
parent | bbfe3c9c49523d3987a3144da119d8f6afd09cf9 (diff) |
Merge with trunk up to r38584.
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/addon_utils.py | 12 | ||||
-rw-r--r-- | release/scripts/modules/bpy/path.py | 13 | ||||
-rw-r--r-- | release/scripts/modules/bpy/utils.py | 9 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/image_utils.py | 6 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/io_utils.py | 41 | ||||
-rw-r--r-- | release/scripts/modules/bpy_extras/mesh_utils.py | 73 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/image.py | 2 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/object_align.py | 201 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/uvcalc_smart_project.py | 10 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_material.py | 5 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_texture.py | 10 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_node.py | 3 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_userpref.py | 18 | ||||
-rw-r--r-- | release/scripts/templates/batch_export.py | 33 |
14 files changed, 363 insertions, 73 deletions
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index 07f1dc618dc..cf74282d064 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -31,6 +31,8 @@ __all__ = ( import bpy as _bpy +error_duplicates = False + def paths(): # RELEASE SCRIPTS: official scripts distributed in Blender releases paths = _bpy.utils.script_paths("addons") @@ -47,8 +49,11 @@ def paths(): def modules(module_cache): + global error_duplicates import os + error_duplicates = False + path_list = paths() # fake module importing @@ -117,7 +122,12 @@ def modules(module_cache): modules_stale -= {mod_name} mod = module_cache.get(mod_name) if mod: - if mod.__time__ != os.path.getmtime(mod_path): + if mod.__file__ != mod_path: + print("multiple addons with the same name:\n %r\n %r" % + (mod.__file__, mod_path)) + error_duplicates = True + + elif mod.__time__ != os.path.getmtime(mod_path): print("reloading addon:", mod_name, mod.__time__, os.path.getmtime(mod_path), mod_path) del module_cache[mod_name] mod = None diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index 5e95428f641..f6254efac2e 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -35,7 +35,7 @@ def abspath(path, start=None): :type start: string """ if path.startswith("//"): - return _os.path.join(_os.path.dirname(_bpy.data.filepath if start is None else start), path[2:]) + return _os.path.join(_os.path.dirname(_bpy.data.filepath) if start is None else start, path[2:]) return path @@ -117,7 +117,7 @@ def display_name_from_filepath(name): """ Returns the path stripped of directort and extension, ensured to be utf8 compatible. """ - return _os.path.splitext(_os.path.basename(name))[0].encode("utf8", "replace").decode("utf8") + return _os.path.splitext(basename(name))[0].encode("utf8", "replace").decode("utf8") def resolve_ncase(path): @@ -231,3 +231,12 @@ def module_names(path, recursive=False): modules.append(("%s.%s" % (filename, mod_name), mod_path)) return modules + + +def basename(path): + """ + Equivalent to os.path.basename, but skips a "//" suffix. + + Use for Windows compatibility. + """ + return _os.path.basename(path[2:] if path.startswith("//") else path) diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py index 7c0d3d24cba..57d3e6dd703 100644 --- a/release/scripts/modules/bpy/utils.py +++ b/release/scripts/modules/bpy/utils.py @@ -298,11 +298,18 @@ _presets = _os.path.join(_scripts[0], "presets") # FIXME - multiple paths def preset_paths(subdir): """ Returns a list of paths for a specific preset. + + :arg subdir: preset subdirectory (must not be an absolute path). + :type subdir: string + :return: script paths. + :rtype: list """ dirs = [] for path in script_paths("presets", all=True): directory = _os.path.join(path, subdir) - if _os.path.isdir(directory): + if not directory.startswith(path): + raise Exception("invalid subdir given %r" % subdir) + elif _os.path.isdir(directory): dirs.append(directory) return dirs diff --git a/release/scripts/modules/bpy_extras/image_utils.py b/release/scripts/modules/bpy_extras/image_utils.py index f91535a0ad4..e56c1c651c4 100644 --- a/release/scripts/modules/bpy_extras/image_utils.py +++ b/release/scripts/modules/bpy_extras/image_utils.py @@ -86,7 +86,9 @@ def load_image(imagepath, variants = [imagepath] if dirname: - variants += [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] + variants += [os.path.join(dirname, imagepath), + os.path.join(dirname, bpy.path.basename(imagepath)), + ] for filepath_test in variants: if ncase_cmp: @@ -99,7 +101,7 @@ def load_image(imagepath, return _image_load(nfilepath) if place_holder: - image = bpy.data.images.new(os.path.basename(imagepath), 128, 128) + image = bpy.data.images.new(bpy.path.basename(imagepath), 128, 128) # allow the path to be resolved later image.filepath = imagepath return image diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index cfa2233f7b6..9545e20b025 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -22,6 +22,7 @@ __all__ = ( "ExportHelper", "ImportHelper", "axis_conversion", + "axis_conversion_ensure", "create_derived_objects", "free_derived_objects", "unpack_list", @@ -154,12 +155,50 @@ def axis_conversion(from_forward='Y', from_up='Z', to_forward='Y', to_up='Z'): if from_forward == to_forward and from_up == to_up: return Matrix().to_3x3() + if from_forward[-1] == from_up[-1] or to_forward[-1] == to_up[-1]: + raise Exception("invalid axis arguments passed, " + "can't use up/forward on the same axis.") + value = reduce(int.__or__, (_axis_convert_num[a] << (i * 3) for i, a in enumerate((from_forward, from_up, to_forward, to_up)))) for i, axis_lut in enumerate(_axis_convert_lut): if value in axis_lut: return Matrix(_axis_convert_matrix[i]) - assert("internal error") + assert(0) + + +def axis_conversion_ensure(operator, forward_attr, up_attr): + """ + Function to ensure an operator has valid axis conversion settings, intended + to be used from :class:`Operator.check`. + + :arg operator: the operator to access axis attributes from. + :type operator: :class:`Operator` + :arg forward_attr: + :type forward_attr: string + :arg up_attr: the directory the *filepath* will be referenced from (normally the export path). + :type up_attr: string + :return: True if the value was modified. + :rtype: boolean + """ + def validate(axis_forward, axis_up): + if axis_forward[-1] == axis_up[-1]: + axis_up = axis_up[0:-1] + 'XYZ'[('XYZ'.index(axis_up[-1]) + 1) % 3] + + return axis_forward, axis_up + + change = False + + axis = getattr(operator, forward_attr), getattr(operator, up_attr) + axis_new = validate(*axis) + + if axis != axis_new: + setattr(operator, forward_attr, axis_new[0]) + setattr(operator, up_attr, axis_new[1]) + + return True + else: + return False # return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() diff --git a/release/scripts/modules/bpy_extras/mesh_utils.py b/release/scripts/modules/bpy_extras/mesh_utils.py index 34925ccb5e9..c42d3d0236a 100644 --- a/release/scripts/modules/bpy_extras/mesh_utils.py +++ b/release/scripts/modules/bpy_extras/mesh_utils.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8 compliant> +# <pep8-80 compliant> __all__ = ( "mesh_linked_faces", @@ -25,6 +25,7 @@ __all__ = ( "edge_loops_from_faces", "edge_loops_from_edges", "ngon_tesselate", + "face_random_points", ) @@ -67,7 +68,8 @@ def mesh_linked_faces(mesh): if mapped_index != nxt_mapped_index: ok = True - # Assign mapping to this group so they all map to this group + # 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 @@ -433,3 +435,70 @@ def ngon_tesselate(from_data, indices, fix_loops=True): fill[i] = tuple([ii for ii in reversed(fi)]) return fill + + +def face_random_points(num_points, faces): + """ + Generates a list of random points over mesh faces. + + :arg num_points: the number of random points to generate on each face. + :type int: + :arg faces: list of the faces to generate points on. + :type faces: :class:`MeshFaces`, sequence + :return: list of random points over all faces. + :rtype: list + """ + + from random import random + from mathutils.geometry import area_tri + + # Split all quads into 2 tris, tris remain unchanged + tri_faces = [] + for f in faces: + tris = [] + verts = f.id_data.vertices + fv = f.vertices[:] + tris.append((verts[fv[0]].co, + verts[fv[1]].co, + verts[fv[2]].co, + )) + if len(fv) == 4: + tris.append((verts[fv[0]].co, + verts[fv[3]].co, + verts[fv[2]].co, + )) + tri_faces.append(tris) + + # For each face, generate the required number of random points + sampled_points = [None] * (num_points * len(faces)) + for i, tf in enumerate(tri_faces): + for k in range(num_points): + # If this is a quad, we need to weight its 2 tris by their area + if len(tf) != 1: + area1 = area_tri(*tf[0]) + area2 = area_tri(*tf[1]) + area_tot = area1 + area2 + + area1 = area1 / area_tot + area2 = area2 / area_tot + + vecs = tf[0 if (random() < area1) else 1] + else: + vecs = tf[0] + + u1 = random() + u2 = random() + u_tot = u1 + u2 + + if u_tot > 1: + u1 = 1.0 - u1 + u2 = 1.0 - u2 + + side1 = vecs[1] - vecs[0] + side2 = vecs[2] - vecs[0] + + p = vecs[0] + u1 * side1 + u2 * side2 + + sampled_points[num_points * i + k] = p + + return sampled_points diff --git a/release/scripts/startup/bl_operators/image.py b/release/scripts/startup/bl_operators/image.py index 34c5b0d922a..4bb53f776ba 100644 --- a/release/scripts/startup/bl_operators/image.py +++ b/release/scripts/startup/bl_operators/image.py @@ -163,7 +163,7 @@ class ProjectEdit(bpy.types.Operator): filepath_final = filepath + ("%.3d.%s" % (i, EXT)) i += 1 - image_new.name = os.path.basename(filepath_final) + image_new.name = bpy.path.basename(filepath_final) ProjectEdit._proj_hack[0] = image_new.name image_new.filepath_raw = filepath_final # TODO, filepath raw is crummy diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py index 3202a717001..d215f3476cf 100644 --- a/release/scripts/startup/bl_operators/object_align.py +++ b/release/scripts/startup/bl_operators/object_align.py @@ -21,13 +21,102 @@ import bpy from mathutils import Vector - -def align_objects(align_x, align_y, align_z, align_mode, relative_to): +def GlobalBB_LQ(bb_world): + + # Initialize the variables with the 8th vertex + left, right, front, back, down, up =\ + bb_world[7][0],\ + bb_world[7][0],\ + bb_world[7][1],\ + bb_world[7][1],\ + bb_world[7][2],\ + bb_world[7][2] + + # Test against the other 7 verts + for i in range (7): + + # X Range + val = bb_world[i][0] + if val < left: + left = val + + if val > right: + right = val + + # Y Range + val = bb_world[i][1] + if val < front: + front = val + + if val > back: + back = val + + # Z Range + val = bb_world[i][2] + if val < down: + down = val + + if val > up: + up = val + + return (Vector((left, front, up)), Vector((right, back, down))) + +def GlobalBB_HQ(obj): + + matrix_world = obj.matrix_world.copy() + + # Initialize the variables with the last vertex + + verts = obj.data.vertices + + val = verts[-1].co * matrix_world + + left, right, front, back, down, up =\ + val[0],\ + val[0],\ + val[1],\ + val[1],\ + val[2],\ + val[2] + + # Test against all other verts + for i in range (len(verts)-1): + + vco = verts[i].co * matrix_world + + # X Range + val = vco[0] + if val < left: + left = val + + if val > right: + right = val + + # Y Range + val = vco[1] + if val < front: + front = val + + if val > back: + back = val + + # Z Range + val = vco[2] + if val < down: + down = val + + if val > up: + up = val + + return (Vector((left, front, up)), Vector((right, back, down))) + + +def align_objects(align_x, align_y, align_z, align_mode, relative_to, bb_quality): cursor = bpy.context.scene.cursor_location - Left_Up_Front_SEL = [0.0, 0.0, 0.0] - Right_Down_Back_SEL = [0.0, 0.0, 0.0] + Left_Front_Up_SEL = [0.0, 0.0, 0.0] + Right_Back_Down_SEL = [0.0, 0.0, 0.0] flag_first = True @@ -42,78 +131,89 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to): return False for obj, bb_world in objs: - Left_Up_Front = bb_world[1] - Right_Down_Back = bb_world[7] + + if bb_quality: + GBB = GlobalBB_HQ(obj) + else: + GBB = GlobalBB_LQ(bb_world) + + Left_Front_Up = GBB[0] + Right_Back_Down = GBB[1] # Active Center if obj == bpy.context.active_object: - center_active_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0 - center_active_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0 - center_active_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0 + center_active_x = (Left_Front_Up[0] + Right_Back_Down[0]) / 2.0 + center_active_y = (Left_Front_Up[1] + Right_Back_Down[1]) / 2.0 + center_active_z = (Left_Front_Up[2] + Right_Back_Down[2]) / 2.0 - size_active_x = (Right_Down_Back[0] - Left_Up_Front[0]) / 2.0 - size_active_y = (Right_Down_Back[1] - Left_Up_Front[1]) / 2.0 - size_active_z = (Left_Up_Front[2] - Right_Down_Back[2]) / 2.0 + size_active_x = (Right_Back_Down[0] - Left_Front_Up[0]) / 2.0 + size_active_y = (Right_Back_Down[1] - Left_Front_Up[1]) / 2.0 + size_active_z = (Left_Front_Up[2] - Right_Back_Down[2]) / 2.0 # Selection Center if flag_first: flag_first = False - Left_Up_Front_SEL[0] = Left_Up_Front[0] - Left_Up_Front_SEL[1] = Left_Up_Front[1] - Left_Up_Front_SEL[2] = Left_Up_Front[2] + Left_Front_Up_SEL[0] = Left_Front_Up[0] + Left_Front_Up_SEL[1] = Left_Front_Up[1] + Left_Front_Up_SEL[2] = Left_Front_Up[2] - Right_Down_Back_SEL[0] = Right_Down_Back[0] - Right_Down_Back_SEL[1] = Right_Down_Back[1] - Right_Down_Back_SEL[2] = Right_Down_Back[2] + Right_Back_Down_SEL[0] = Right_Back_Down[0] + Right_Back_Down_SEL[1] = Right_Back_Down[1] + Right_Back_Down_SEL[2] = Right_Back_Down[2] else: # X axis - if Left_Up_Front[0] < Left_Up_Front_SEL[0]: - Left_Up_Front_SEL[0] = Left_Up_Front[0] + if Left_Front_Up[0] < Left_Front_Up_SEL[0]: + Left_Front_Up_SEL[0] = Left_Front_Up[0] # Y axis - if Left_Up_Front[1] < Left_Up_Front_SEL[1]: - Left_Up_Front_SEL[1] = Left_Up_Front[1] + if Left_Front_Up[1] < Left_Front_Up_SEL[1]: + Left_Front_Up_SEL[1] = Left_Front_Up[1] # Z axis - if Left_Up_Front[2] > Left_Up_Front_SEL[2]: - Left_Up_Front_SEL[2] = Left_Up_Front[2] + if Left_Front_Up[2] > Left_Front_Up_SEL[2]: + Left_Front_Up_SEL[2] = Left_Front_Up[2] # X axis - if Right_Down_Back[0] > Right_Down_Back_SEL[0]: - Right_Down_Back_SEL[0] = Right_Down_Back[0] + if Right_Back_Down[0] > Right_Back_Down_SEL[0]: + Right_Back_Down_SEL[0] = Right_Back_Down[0] # Y axis - if Right_Down_Back[1] > Right_Down_Back_SEL[1]: - Right_Down_Back_SEL[1] = Right_Down_Back[1] + if Right_Back_Down[1] > Right_Back_Down_SEL[1]: + Right_Back_Down_SEL[1] = Right_Back_Down[1] # Z axis - if Right_Down_Back[2] < Right_Down_Back_SEL[2]: - Right_Down_Back_SEL[2] = Right_Down_Back[2] + if Right_Back_Down[2] < Right_Back_Down_SEL[2]: + Right_Back_Down_SEL[2] = Right_Back_Down[2] - center_sel_x = (Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0]) / 2.0 - center_sel_y = (Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1]) / 2.0 - center_sel_z = (Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2]) / 2.0 + center_sel_x = (Left_Front_Up_SEL[0] + Right_Back_Down_SEL[0]) / 2.0 + center_sel_y = (Left_Front_Up_SEL[1] + Right_Back_Down_SEL[1]) / 2.0 + center_sel_z = (Left_Front_Up_SEL[2] + Right_Back_Down_SEL[2]) / 2.0 # Main Loop for obj, bb_world in objs: bb_world = [Vector(v[:]) * obj.matrix_world for v in obj.bound_box] + + if bb_quality: + GBB = GlobalBB_HQ(obj) + else: + GBB = GlobalBB_LQ(bb_world) + + Left_Front_Up = GBB[0] + Right_Back_Down = GBB[1] - Left_Up_Front = bb_world[1] - Right_Down_Back = bb_world[7] - - center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0 - center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0 - center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0 + center_x = (Left_Front_Up[0] + Right_Back_Down[0]) / 2.0 + center_y = (Left_Front_Up[1] + Right_Back_Down[1]) / 2.0 + center_z = (Left_Front_Up[2] + Right_Back_Down[2]) / 2.0 - positive_x = Right_Down_Back[0] - positive_y = Right_Down_Back[1] - positive_z = Left_Up_Front[2] + positive_x = Right_Back_Down[0] + positive_y = Right_Back_Down[1] + positive_z = Left_Front_Up[2] - negative_x = Left_Up_Front[0] - negative_y = Left_Up_Front[1] - negative_z = Right_Down_Back[2] + negative_x = Left_Front_Up[0] + negative_y = Left_Front_Up[1] + negative_z = Right_Back_Down[2] obj_loc = obj.location @@ -228,7 +328,7 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to): return True -from bpy.props import EnumProperty +from bpy.props import EnumProperty, BoolProperty class AlignObjects(bpy.types.Operator): @@ -237,6 +337,11 @@ class AlignObjects(bpy.types.Operator): bl_label = "Align Objects" bl_options = {'REGISTER', 'UNDO'} + bb_quality = BoolProperty( + name="High Quality", + description="Enables high quality calculation of the bounding box for perfect results on complex shape meshes with rotation/scale (Slow)", + default=False) + align_mode = EnumProperty(items=( ('OPT_1', "Negative Sides", ""), ('OPT_2', "Centers", ""), @@ -269,10 +374,10 @@ class AlignObjects(bpy.types.Operator): def execute(self, context): align_axis = self.align_axis - ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to) + ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to, self.bb_quality) if not ret: self.report({'WARNING'}, "No objects with bound-box selected") return {'CANCELLED'} else: - return {'FINISHED'} + return {'FINISHED'}
\ No newline at end of file diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py index 7ea89cfa479..9c3be84b807 100644 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py @@ -747,14 +747,8 @@ def packIslands(islandList): def VectoQuat(vec): - 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() + vec = vec.normalized() + return vec.to_track_quat('Z', 'X' if abs(vec.x) > 0.5 else 'Y').inverted() class thickface(object): diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 45c15bd1ce6..2a52ae23782 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -174,6 +174,7 @@ class MATERIAL_PT_pipeline(MaterialButtonsPanel, bpy.types.Panel): row.prop(mat, "use_transparency") sub = row.column() sub.prop(mat, "offset_z") + sub.active = mat_type and mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY' row = layout.row() @@ -199,6 +200,7 @@ class MATERIAL_PT_pipeline(MaterialButtonsPanel, bpy.types.Panel): col.prop(mat, "shadow_cast_alpha", text="Casting Alpha") col.prop(mat, "use_cast_buffer_shadows") col.prop(mat, "use_cast_approximate") + col.prop(mat, "pass_index") class MATERIAL_PT_diffuse(MaterialButtonsPanel, bpy.types.Panel): @@ -729,7 +731,8 @@ class MATERIAL_PT_options(MaterialButtonsPanel, bpy.types.Panel): col.prop(mat, "use_vertex_color_paint") col.prop(mat, "use_vertex_color_light") col.prop(mat, "use_object_color") - col.prop(mat, "pass_index") + if simple_material(base_mat): + col.prop(mat, "pass_index") class MATERIAL_PT_shadow(MaterialButtonsPanel, bpy.types.Panel): diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index f0265f8db67..7ca8818cbd2 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -88,15 +88,15 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel, bpy.types.Panel): @classmethod def poll(cls, context): engine = context.scene.render.engine - if not hasattr(context, "texture_slot"): + if not (hasattr(context, "texture_slot") or hasattr(context, "texture_node")): return False return ((context.material or context.world or context.lamp or context.brush or context.texture or context.particle_system or isinstance(context.space_data.pin_id, bpy.types.ParticleSettings)) and (engine in cls.COMPAT_ENGINES)) def draw(self, context): layout = self.layout - slot = context.texture_slot - node = context.texture_node + slot = getattr(context, "texture_slot", None) + node = getattr(context, "texture_node", None) space = context.space_data tex = context.texture idblock = context_tex_datablock(context) @@ -393,7 +393,7 @@ class TEXTURE_PT_image_sampling(TextureTypePanel, bpy.types.Panel): idblock = context_tex_datablock(context) tex = context.texture - slot = context.texture_slot + slot = getattr(context, "texture_slot", None) split = layout.split() @@ -408,7 +408,7 @@ class TEXTURE_PT_image_sampling(TextureTypePanel, bpy.types.Panel): col = split.column() #Only for Material based textures, not for Lamp/World... - if isinstance(idblock, bpy.types.Material): + if slot and isinstance(idblock, bpy.types.Material): col.prop(tex, "use_normal_map") row = col.row() row.active = tex.use_normal_map diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index fed1cc49c4c..831fd359782 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -135,9 +135,10 @@ class NODE_MT_node(bpy.types.Menu): layout.operator("transform.resize") layout.separator() - + layout.operator("node.duplicate_move") layout.operator("node.delete") + layout.operator("node.delete_reconnect") layout.separator() layout.operator("node.link_make") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index d4592e6e58c..886c70972b5 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -890,6 +890,16 @@ class USERPREF_PT_addons(bpy.types.Panel): return True return False + @staticmethod + def draw_error(layout, message): + lines = message.split("\n") + box = layout.box() + rowsub = box.row() + rowsub.label(lines[0]) + rowsub.label(icon='ERROR') + for l in lines[1:]: + box.label(l) + def draw(self, context): layout = self.layout @@ -910,6 +920,14 @@ class USERPREF_PT_addons(bpy.types.Panel): col = split.column() + # set in addon_utils.modules(...) + if addon_utils.error_duplicates: + self.draw_error(col, + "Multiple addons using the same name found!\n" + "likely a problem with the script search path.\n" + "(see console for details)", + ) + filter = context.window_manager.addon_filter search = context.window_manager.addon_search.lower() support = context.window_manager.addon_support diff --git a/release/scripts/templates/batch_export.py b/release/scripts/templates/batch_export.py new file mode 100644 index 00000000000..aa0e601725b --- /dev/null +++ b/release/scripts/templates/batch_export.py @@ -0,0 +1,33 @@ +# exports each selected object into its own file + +import bpy +import os + +# export to blend file location +basedir = os.path.dirname(bpy.data.filepath) + +if not basedir: + raise Exception("Blend file is not saved") + +selection = bpy.context.selected_objects + +bpy.ops.object.select_all(action='DESELECT') + +for obj in selection: + + obj.select = True + + name = bpy.path.clean_name(obj.name) + fn = os.path.join(basedir, name) + + bpy.ops.export_scene.fbx(filepath=fn + ".fbx", use_selection=True) + + ## Can be used for multiple formats + # bpy.ops.export_scene.x3d(filepath=fn + ".x3d", use_selection=True) + + obj.select = False + + print("written:", fn) + +for obj in selection: + obj.select = True |