diff options
Diffstat (limited to 'release/scripts')
159 files changed, 7483 insertions, 2993 deletions
diff --git a/release/scripts/io/engine_render_pov.py b/release/scripts/io/engine_render_pov.py index 338676968cd..9cda4375ecc 100644 --- a/release/scripts/io/engine_render_pov.py +++ b/release/scripts/io/engine_render_pov.py @@ -134,7 +134,7 @@ def write_pov(filename, scene=None, info_callback=None): def exportCamera(): camera = scene.camera - matrix = camera.matrix + matrix = camera.matrix_world # compute resolution Qsize = float(render.resolution_x) / float(render.resolution_y) @@ -155,7 +155,7 @@ def write_pov(filename, scene=None, info_callback=None): for ob in lamps: lamp = ob.data - matrix = ob.matrix + matrix = ob.matrix_world color = tuple([c * lamp.energy for c in lamp.color]) # Colour is modified by energy @@ -263,11 +263,11 @@ def write_pov(filename, scene=None, info_callback=None): writeObjectMaterial(material) - writeMatrix(ob.matrix) + writeMatrix(ob.matrix_world) file.write('}\n') - def exportMeshs(sel): + def exportMeshs(scene, sel): ob_num = 0 @@ -280,7 +280,7 @@ def write_pov(filename, scene=None, info_callback=None): me = ob.data me_materials = me.materials - me = ob.create_mesh(True, 'RENDER') + me = ob.create_mesh(scene, True, 'RENDER') if not me: continue @@ -292,7 +292,7 @@ def write_pov(filename, scene=None, info_callback=None): # continue # me = ob.data - matrix = ob.matrix + matrix = ob.matrix_world try: uv_layer = me.active_uv_texture.data except: @@ -545,7 +545,7 @@ def write_pov(filename, scene=None, info_callback=None): mist = world.mist - if mist.enabled: + if mist.use_mist: file.write('fog {\n') file.write('\tdistance %.6f\n' % mist.depth) file.write('\tcolor rgbt<%.3g, %.3g, %.3g, %.3g>\n' % (tuple(world.horizon_color) + (1 - mist.intensity,))) @@ -593,7 +593,7 @@ def write_pov(filename, scene=None, info_callback=None): sel = scene.objects exportLamps([l for l in sel if l.type == 'LAMP']) exportMeta([l for l in sel if l.type == 'META']) - exportMeshs(sel) + exportMeshs(scene, sel) exportWorld(scene.world) exportGlobalSettings(scene) @@ -629,8 +629,8 @@ def write_pov_ini(filename_ini, filename_pov, filename_image): file.write('Output_File_Type=T\n') # TGA, best progressive loading file.write('Output_Alpha=1\n') - if render.antialiasing: - aa_mapping = {'OVERSAMPLE_5': 2, 'OVERSAMPLE_8': 3, 'OVERSAMPLE_11': 4, 'OVERSAMPLE_16': 5} # method 1 assumed + if render.render_antialiasing: + aa_mapping = {'5': 2, '8': 3, '11': 4, '16': 5} # method 1 assumed file.write('Antialias=1\n') file.write('Antialias_Depth=%d\n' % aa_mapping[render.antialiasing_samples]) else: @@ -762,12 +762,22 @@ class PovrayRender(bpy.types.RenderEngine): if 1: # TODO, when povray isnt found this gives a cryptic error, would be nice to be able to detect if it exists - self._process = subprocess.Popen([pov_binary, self._temp_file_ini]) # stdout=subprocess.PIPE, stderr=subprocess.PIPE + try: + self._process = subprocess.Popen([pov_binary, self._temp_file_ini]) # stdout=subprocess.PIPE, stderr=subprocess.PIPE + except OSError: + # TODO, report api + print("POVRAY: could not execute '%s', possibly povray isn't installed" % pov_binary) + import traceback + traceback.print_exc() + print ("***-DONE-***") + return False + else: # This works too but means we have to wait until its done os.system('%s %s' % (pov_binary, self._temp_file_ini)) print ("***-DONE-***") + return True def _cleanup(self): for f in (self._temp_file_in, self._temp_file_ini, self._temp_file_out): @@ -783,7 +793,10 @@ class PovrayRender(bpy.types.RenderEngine): self.update_stats("", "POVRAY: Exporting data from Blender") self._export(scene) self.update_stats("", "POVRAY: Parsing File") - self._render() + + if not self._render(): + self.update_stats("", "POVRAY: Not found") + return r = scene.render @@ -880,6 +893,30 @@ for member in dir(properties_material): except: pass del properties_material +import properties_data_mesh +for member in dir(properties_data_mesh): + subclass = getattr(properties_data_mesh, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except: + pass +del properties_data_mesh +import properties_texture +for member in dir(properties_texture): + subclass = getattr(properties_texture, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except: + pass +del properties_texture +import properties_data_camera +for member in dir(properties_data_camera): + subclass = getattr(properties_data_camera, member) + try: + subclass.COMPAT_ENGINES.add('POVRAY_RENDER') + except: + pass +del properties_data_camera class RenderButtonsPanel(bpy.types.Panel): @@ -954,6 +991,7 @@ def register(): for cls in classes: register(cls) + def unregister(): unregister = bpy.types.unregister for cls in classes: @@ -961,4 +999,3 @@ def unregister(): if __name__ == "__main__": register() - diff --git a/release/scripts/io/export_3ds.py b/release/scripts/io/export_3ds.py index b65332c931f..4fd889c75c6 100644 --- a/release/scripts/io/export_3ds.py +++ b/release/scripts/io/export_3ds.py @@ -74,15 +74,15 @@ import bpy # also used by X3D exporter # return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() -def create_derived_objects(ob): +def create_derived_objects(scene, ob): if ob.parent and ob.parent.dupli_type != 'NONE': return False, None if ob.dupli_type != 'NONE': - ob.create_dupli_list() + ob.create_dupli_list(scene) return True, [(dob.object, dob.matrix) for dob in ob.dupli_list] else: - return False, [(ob, ob.matrix)] + return False, [(ob, ob.matrix_world)] # also used by X3D exporter def free_derived_objects(ob): @@ -494,8 +494,7 @@ def make_material_texture_chunk(id, images): mat_sub = _3ds_chunk(id) def add_image(img): - filename = os.path.basename(image.filename) -# filename = image.filename.split('\\')[-1].split('/')[-1] + filename = os.path.basename(image.filepath) mat_sub_file = _3ds_chunk(MATMAPFILE) mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename))) mat_sub.add_subchunk(mat_sub_file) @@ -942,6 +941,8 @@ def save_3ds(filename, context): sce = context.scene # sce= bpy.data.scenes.active + bpy.ops.object.mode_set(mode='OBJECT') + # Initialize the main chunk (primary): primary = _3ds_chunk(PRIMARY) # Add version chunk: @@ -966,11 +967,12 @@ def save_3ds(filename, context): # each material is added once): materialDict = {} mesh_objects = [] - for ob in [ob for ob in context.scene.objects if ob.is_visible()]: + scene = context.scene + for ob in [ob for ob in scene.objects if ob.is_visible(scene)]: # for ob in sce.objects.context: # get derived objects - free, derived = create_derived_objects(ob) + free, derived = create_derived_objects(scene, ob) if derived == None: continue @@ -980,7 +982,7 @@ def save_3ds(filename, context): if ob.type not in ('MESH', 'CURVE', 'SURFACE', 'TEXT', 'META'): continue - data = ob_derived.create_mesh(True, 'PREVIEW') + data = ob_derived.create_mesh(scene, True, 'PREVIEW') # data = getMeshFromObject(ob_derived, None, True, False, sce) if data: data.transform(mat) @@ -1120,12 +1122,11 @@ class Export3DS(bpy.types.Operator): # to the class instance from the operator settings before calling. - # filename = StringProperty(name="File Name", description="File name used for exporting the 3DS file", maxlen= 1024, default= ""), - path = StringProperty(name="File Path", description="File path used for exporting the 3DS file", maxlen= 1024, default= "") + filepath = StringProperty(name="File Path", description="Filepath used for exporting the 3DS file", maxlen= 1024, default= "") check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'}) def execute(self, context): - save_3ds(self.properties.path, context) + save_3ds(self.properties.filepath, context) return {'FINISHED'} def invoke(self, context, event): @@ -1139,8 +1140,8 @@ class Export3DS(bpy.types.Operator): # Add to a menu def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".3ds") - self.layout.operator(Export3DS.bl_idname, text="3D Studio (.3ds)").path = default_path + default_path = os.path.splitext(bpy.data.filepath)[0] + ".3ds" + self.layout.operator(Export3DS.bl_idname, text="3D Studio (.3ds)").filepath = default_path def register(): @@ -1153,4 +1154,3 @@ def unregister(): if __name__ == "__main__": register() - diff --git a/release/scripts/io/export_fbx.py b/release/scripts/io/export_fbx.py index e1c7f68c557..4505b56b41c 100644 --- a/release/scripts/io/export_fbx.py +++ b/release/scripts/io/export_fbx.py @@ -54,50 +54,11 @@ import time import math # math.pi import shutil # for file copying - - - - - - - - - - - - - - - - - - - - - -# import Blender import bpy -import Mathutils - - - - - - - - - - - - - - - - - - +from mathutils import Vector, Euler, Matrix, RotationMatrix def copy_file(source, dest): + # XXX - remove, can use shutil file = open(source, 'rb') data = file.read() file.close() @@ -114,7 +75,7 @@ def copy_images(dest_dir, textures): image_paths = set() for tex in textures: - image_paths.add(Blender.sys.expandpath(tex.filename)) + image_paths.add(bpy.utils.expandpath(tex.filepath)) # Now copy images copyCount = 0 @@ -135,7 +96,7 @@ def copy_images(dest_dir, textures): # I guess FBX uses degrees instead of radians (Arystan). # Call this function just before writing to FBX. def eulerRadToDeg(eul): - ret = Mathutils.Euler() + ret = Euler() ret.x = 180 / math.pi * eul[0] ret.y = 180 / math.pi * eul[1] @@ -143,10 +104,10 @@ def eulerRadToDeg(eul): return ret -mtx4_identity = Mathutils.Matrix() +mtx4_identity = Matrix() # testing -mtx_x90 = Mathutils.RotationMatrix( math.pi/2, 3, 'X') # used +mtx_x90 = RotationMatrix( math.pi/2, 3, 'X') # used #mtx_x90n = RotationMatrix(-90, 3, 'x') #mtx_y90 = RotationMatrix( 90, 3, 'y') #mtx_y90n = RotationMatrix(-90, 3, 'y') @@ -154,11 +115,11 @@ mtx_x90 = Mathutils.RotationMatrix( math.pi/2, 3, 'X') # used #mtx_z90n = RotationMatrix(-90, 3, 'z') #mtx4_x90 = RotationMatrix( 90, 4, 'x') -mtx4_x90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'X') # used +mtx4_x90n = RotationMatrix(-math.pi/2, 4, 'X') # used #mtx4_y90 = RotationMatrix( 90, 4, 'y') -mtx4_y90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'Y') # used -mtx4_z90 = Mathutils.RotationMatrix( math.pi/2, 4, 'Z') # used -mtx4_z90n = Mathutils.RotationMatrix(-math.pi/2, 4, 'Z') # used +mtx4_y90n = RotationMatrix(-math.pi/2, 4, 'Y') # used +mtx4_z90 = RotationMatrix( math.pi/2, 4, 'Z') # used +mtx4_z90n = RotationMatrix(-math.pi/2, 4, 'Z') # used # def strip_path(p): # return p.split('\\')[-1].split('/')[-1] @@ -333,7 +294,7 @@ def write(filename, batch_objects = None, \ EXP_CAMERA = True, EXP_EMPTY = True, EXP_IMAGE_COPY = False, - GLOBAL_MATRIX = Mathutils.Matrix(), + GLOBAL_MATRIX = Matrix(), ANIM_ENABLE = True, ANIM_OPTIMIZE = True, ANIM_OPTIMIZE_PRECISSION = 6, @@ -344,6 +305,8 @@ def write(filename, batch_objects = None, \ BATCH_OWN_DIR = False ): + bpy.ops.object.mode_set(mode='OBJECT') + # ----------------- Batch support! if BATCH_ENABLE: if os == None: BATCH_OWN_DIR = False @@ -388,7 +351,6 @@ def write(filename, batch_objects = None, \ orig_sce = context.scene # orig_sce = bpy.data.scenes.active - new_fbxpath = fbxpath # own dir option modifies, we need to keep an original for data in data_seq: # scene or group newname = BATCH_FILE_PREFIX + bpy.utils.clean_name(data.name) @@ -545,10 +507,10 @@ def write(filename, batch_objects = None, \ #arm_mat = self.fbxArm.parRelMatrix() if not self.parent: #return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat) # dont apply arm matrix anymore - return mtx4_z90 * self.getPoseMatrix(frame) + return self.getPoseMatrix(frame) * mtx4_z90 else: #return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert() - return (mtx4_z90 * (self.getPoseMatrix(frame))) * (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert() + return (self.parent.getPoseMatrix(frame) * mtx4_z90).invert() * ((self.getPoseMatrix(frame)) * mtx4_z90) # we need thes because cameras and lights modified rotations def getAnimParRelMatrixRot(self, frame): @@ -565,42 +527,41 @@ def write(filename, batch_objects = None, \ self.blenObject = ob self.fbxGroupNames = [] self.fbxParent = None # set later on IF the parent is in the selection. - if matrixWorld: self.matrixWorld = matrixWorld * GLOBAL_MATRIX - else: self.matrixWorld = ob.matrix * GLOBAL_MATRIX + if matrixWorld: self.matrixWorld = GLOBAL_MATRIX * matrixWorld + else: self.matrixWorld = GLOBAL_MATRIX * ob.matrix_world # else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX self.__anim_poselist = {} # we should only access this def parRelMatrix(self): if self.fbxParent: - return self.matrixWorld * self.fbxParent.matrixWorld.copy().invert() + return self.fbxParent.matrixWorld.copy().invert() * self.matrixWorld else: return self.matrixWorld def setPoseFrame(self, f): - self.__anim_poselist[f] = self.blenObject.matrix.copy() -# self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() + self.__anim_poselist[f] = self.blenObject.matrix_world.copy() def getAnimParRelMatrix(self, frame): if self.fbxParent: #return (self.__anim_poselist[frame] * self.fbxParent.__anim_poselist[frame].copy().invert() ) * GLOBAL_MATRIX - return (self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert() + return (GLOBAL_MATRIX * self.fbxParent.__anim_poselist[frame]).invert() * (GLOBAL_MATRIX * self.__anim_poselist[frame]) else: - return self.__anim_poselist[frame] * GLOBAL_MATRIX + return GLOBAL_MATRIX * self.__anim_poselist[frame] def getAnimParRelMatrixRot(self, frame): type = self.blenObject.type if self.fbxParent: - matrix_rot = (((self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert())).rotation_part() + matrix_rot = ((GLOBAL_MATRIX * self.fbxParent.__anim_poselist[frame]).invert() * (GLOBAL_MATRIX * self.__anim_poselist[frame])).rotation_part() else: - matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotation_part() + matrix_rot = (GLOBAL_MATRIX * self.__anim_poselist[frame]).rotation_part() # Lamps need to be rotated if type =='LAMP': - matrix_rot = mtx_x90 * matrix_rot + matrix_rot = matrix_rot * mtx_x90 elif type =='CAMERA': # elif ob and type =='Camera': - y = Mathutils.Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, y) + y = matrix_rot * Vector((0.0, 1.0, 0.0)) + matrix_rot = RotationMatrix(math.pi/2, 3, y) * matrix_rot return matrix_rot @@ -651,7 +612,7 @@ def write(filename, batch_objects = None, \ }''' % (curtime)) file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime) - file.write('\nCreator: "Blender3D version %s"' % bpy.app.version_string) + file.write('\nCreator: "Blender version %s"' % bpy.app.version_string) pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way @@ -666,15 +627,15 @@ def write(filename, batch_objects = None, \ # we know we have a matrix # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod) - matrix = mtx4_z90 * ob.matrix_local # dont apply armature matrix anymore + matrix = ob.matrix_local * mtx4_z90 # dont apply armature matrix anymore # matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore parent = ob.parent if parent: #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod) - par_matrix = mtx4_z90 * parent.matrix_local # dont apply armature matrix anymore + par_matrix = parent.matrix_local * mtx4_z90 # dont apply armature matrix anymore # par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore - matrix = matrix * par_matrix.copy().invert() + matrix = par_matrix.copy().invert() * matrix matrix_rot = matrix.rotation_part() @@ -684,7 +645,7 @@ def write(filename, batch_objects = None, \ else: # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore - #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX + #if ob and not matrix: matrix = ob.matrix_world * GLOBAL_MATRIX if ob and not matrix: raise Exception("error: this should never happen!") matrix_rot = matrix @@ -698,11 +659,11 @@ def write(filename, batch_objects = None, \ matrix_rot = matrix.rotation_part() # Lamps need to be rotated if ob and ob.type =='Lamp': - matrix_rot = mtx_x90 * matrix_rot + matrix_rot = matrix_rot * mtx_x90 rot = tuple(matrix_rot.to_euler()) elif ob and ob.type =='Camera': - y = Mathutils.Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, y) + y = matrix_rot * Vector((0.0, 1.0, 0.0)) + matrix_rot = RotationMatrix(math.pi/2, 3, y) * matrix_rot rot = tuple(matrix_rot.to_euler()) else: rot = tuple(matrix_rot.to_euler()) @@ -1087,8 +1048,8 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tTypeFlags: "Camera"') file.write('\n\t\tGeometryVersion: 124') file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc) - file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Mathutils.Vector(0,1,0) * matrix_rot) ) - file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Mathutils.Vector(0,0,-1)*matrix_rot) ) + file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(matrix_rot * Vector((0.0, 1.0, 0.0)))) + file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(matrix_rot * Vector((0.0, 0.0, -1.0)))) #file.write('\n\t\tUp: 0,0,0' ) #file.write('\n\t\tLookAt: 0,0,0' ) @@ -1293,7 +1254,7 @@ def write(filename, batch_objects = None, \ file.write('\n\t}') def copy_image(image): - fn = bpy.utils.expandpath(image.filename) + fn = bpy.utils.expandpath(image.filepath) fn_strip = os.path.basename(fn) if EXP_IMAGE_COPY: @@ -1320,7 +1281,7 @@ def write(filename, batch_objects = None, \ Property: "Height", "int", "",0''') if tex: fname_rel, fname_strip = copy_image(tex) -# fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) +# fname, fname_strip, fname_rel = derived_paths(tex.filepath, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1385,7 +1346,7 @@ def write(filename, batch_objects = None, \ if tex: fname_rel, fname_strip = copy_image(tex) -# fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY) +# fname, fname_strip, fname_rel = derived_paths(tex.filepath, basepath, EXP_IMAGE_COPY) else: fname = fname_strip = fname_rel = '' @@ -1485,10 +1446,10 @@ def write(filename, batch_objects = None, \ if my_mesh.fbxParent: # TODO FIXME, this case is broken in some cases. skinned meshes just shouldnt have parents where possible! - m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() ) + m = (my_mesh.matrixWorld.copy().invert() * my_bone.fbxArm.matrixWorld.copy() * my_bone.restMatrix) * mtx4_z90 else: # Yes! this is it... - but dosnt work when the mesh is a. - m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() ) + m = (my_mesh.matrixWorld.copy().invert() * my_bone.fbxArm.matrixWorld.copy() * my_bone.restMatrix) * mtx4_z90 #m = mtx4_z90 * my_bone.restMatrix matstr = mat4x4str(m) @@ -1542,17 +1503,14 @@ def write(filename, batch_objects = None, \ file.write('\n\t\tPolygonVertexIndex: ') i=-1 for f in me.faces: - fi = f.verts - # fi = [v_index for j, v_index in enumerate(f.verts) if v_index != 0 or j != 3] -# fi = [v.index for v in f] + fi = f.verts[:] - # flip the last index, odd but it looks like - # this is how fbx tells one face from another - fi[-1] = -(fi[-1]+1) + # last index XORd w. -1 indicates end of face + fi[-1] = fi[-1] ^ -1 fi = tuple(fi) + if i==-1: if len(fi) == 3: file.write('%i,%i,%i' % fi ) -# if len(f) == 3: file.write('%i,%i,%i' % fi ) else: file.write('%i,%i,%i,%i' % fi ) i=0 else: @@ -1560,10 +1518,26 @@ def write(filename, batch_objects = None, \ file.write('\n\t\t') i=0 if len(fi) == 3: file.write(',%i,%i,%i' % fi ) -# if len(f) == 3: file.write(',%i,%i,%i' % fi ) else: file.write(',%i,%i,%i,%i' % fi ) i+=1 + # write loose edges as faces. + for ed in me.edges: + if ed.loose: + ed_val = ed.verts[:] + ed_val = ed_val[0], ed_val[-1] ^ -1 + + if i==-1: + file.write('%i,%i' % ed_val) + i=0 + else: + if i==13: + file.write('\n\t\t') + i=0 + file.write(',%i,%i' % ed_val) + i+=1 + + file.write('\n\t\tEdges: ') i=-1 for ed in me.edges: @@ -2036,12 +2010,11 @@ def write(filename, batch_objects = None, \ if ob_arms_orig_rest: for ob_base in bpy.data.objects: - #if ob_base.type == 'Armature': - ob_base.make_display_list() -# ob_base.makeDisplayList() + if ob_base.type == 'ARMATURE': + ob_base.update(scene) # This causes the makeDisplayList command to effect the mesh - scene.set_frame(scene.current_frame) + scene.set_frame(scene.frame_current) # Blender.Set('curframe', Blender.Get('curframe')) @@ -2051,9 +2024,9 @@ def write(filename, batch_objects = None, \ if ob_base.parent and ob_base.parent.dupli_type != 'NONE': continue - obs = [(ob_base, ob_base.matrix)] + obs = [(ob_base, ob_base.matrix_world)] if ob_base.dupli_type != 'NONE': - ob_base.create_dupli_list() + ob_base.create_dupli_list(scene) obs = [(dob.object, dob.matrix) for dob in ob_base.dupli_list] for ob, mtx in obs: @@ -2082,7 +2055,7 @@ def write(filename, batch_objects = None, \ if tmp_ob_type != 'MESH': # if tmp_ob_type != 'Mesh': # me = bpy.data.meshes.new() - try: me = ob.create_mesh(True, 'PREVIEW') + try: me = ob.create_mesh(scene, True, 'PREVIEW') # try: me.getFromObject(ob) except: me = None if me: @@ -2093,7 +2066,7 @@ def write(filename, batch_objects = None, \ # Mesh Type! if EXP_MESH_APPLY_MOD: # me = bpy.data.meshes.new() - me = ob.create_mesh(True, 'PREVIEW') + me = ob.create_mesh(scene, True, 'PREVIEW') # me.getFromObject(ob) # so we keep the vert groups @@ -2212,11 +2185,9 @@ def write(filename, batch_objects = None, \ if ob_arms_orig_rest: for ob_base in bpy.data.objects: if ob_base.type == 'ARMATURE': -# if ob_base.type == 'Armature': - ob_base.make_display_list() -# ob_base.makeDisplayList() + ob_base.update(scene) # This causes the makeDisplayList command to effect the mesh - scene.set_frame(scene.current_frame) + scene.set_frame(scene.frame_current) # Blender.Set('curframe', Blender.Get('curframe')) del tmp_ob_type, tmp_objects @@ -2701,9 +2672,9 @@ Connections: {''') return int(0.5 + ((t/fps) * 46186158000)) fps = float(render.fps) - start = scene.start_frame + start = scene.frame_start # start = render.sFrame - end = scene.end_frame + end = scene.frame_end # end = render.eFrame if end < start: start, end = end, start if start==end: ANIM_ENABLE = False @@ -2713,7 +2684,7 @@ Connections: {''') if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]: - frame_orig = scene.current_frame + frame_orig = scene.frame_current # frame_orig = Blender.Get('curframe') if ANIM_OPTIMIZE: @@ -2812,7 +2783,7 @@ Takes: {''') # Set the action active for my_bone in ob_arms: if blenAction in my_bone.blenActionList: - ob.action = blenAction + ob.animation_data.action = blenAction # print '\t\tSetting Action!', blenAction # scene.update(1) @@ -2969,7 +2940,7 @@ Takes: {''') # end action loop. set original actions # do this after every loop incase actions effect eachother. for my_bone in ob_arms: - my_bone.blenObject.action = my_bone.blenAction + my_bone.blenObject.animation_data.action = my_bone.blenAction file.write('\n}') @@ -2998,8 +2969,7 @@ Takes: {''') # --------------------------- Footer if world: m = world.mist - has_mist = m.enabled -# has_mist = world.mode & 1 + has_mist = m.use_mist mist_intense = m.intensity mist_start = m.start mist_end = m.depth @@ -3150,9 +3120,9 @@ def fbx_ui_write(filename, context): # Make the matrix GLOBAL_MATRIX = mtx4_identity GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = GLOBALS['_SCALE'].val - if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n - if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n - if GLOBALS['_ZROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n + if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = mtx4_x90n * GLOBAL_MATRIX + if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = mtx4_y90n * GLOBAL_MATRIX + if GLOBALS['_ZROT90'].val: GLOBAL_MATRIX = mtx4_z90n * GLOBAL_MATRIX ret = write(\ filename, None,\ @@ -3364,7 +3334,7 @@ class ExportFBX(bpy.types.Operator): # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for exporting the FBX file", maxlen= 1024, default="") + filepath = StringProperty(name="File Path", description="Filepath used for exporting the FBX file", maxlen= 1024, default="") check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'}) EXP_OBS_SELECTED = BoolProperty(name="Selected Objects", description="Export selected objects on visible layers", default=True) @@ -3398,16 +3368,16 @@ class ExportFBX(bpy.types.Operator): return context.active_object def execute(self, context): - if not self.properties.path: - raise Exception("path not set") + if not self.properties.filepath: + raise Exception("filepath not set") GLOBAL_MATRIX = mtx4_identity GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self.properties.TX_SCALE - if self.properties.TX_XROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n - if self.properties.TX_YROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n - if self.properties.TX_ZROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n + if self.properties.TX_XROT90: GLOBAL_MATRIX = mtx4_x90n * GLOBAL_MATRIX + if self.properties.TX_YROT90: GLOBAL_MATRIX = mtx4_y90n * GLOBAL_MATRIX + if self.properties.TX_ZROT90: GLOBAL_MATRIX = mtx4_z90n * GLOBAL_MATRIX - write(self.properties.path, + write(self.properties.filepath, None, # XXX context, self.properties.EXP_OBS_SELECTED, @@ -3440,7 +3410,7 @@ class ExportFBX(bpy.types.Operator): # if __name__ == "__main__": -# bpy.ops.EXPORT_OT_ply(filename="/tmp/test.ply") +# bpy.ops.EXPORT_OT_ply(filepath="/tmp/test.ply") # NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) @@ -3467,8 +3437,8 @@ class ExportFBX(bpy.types.Operator): def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".fbx") - self.layout.operator(ExportFBX.bl_idname, text="Autodesk FBX (.fbx)").path = default_path + default_path = os.path.splitext(bpy.data.filepath)[0] + ".fbx" + self.layout.operator(ExportFBX.bl_idname, text="Autodesk FBX (.fbx)").filepath = default_path def register(): diff --git a/release/scripts/io/export_mdd.py b/release/scripts/io/export_mdd.py index a4dca4db204..d2e53070910 100644 --- a/release/scripts/io/export_mdd.py +++ b/release/scripts/io/export_mdd.py @@ -48,7 +48,7 @@ Be sure not to use modifiers that change the number or order of verts in the mes # ***** END GPL LICENCE BLOCK ***** import bpy -import Mathutils +import mathutils from struct import pack @@ -82,12 +82,12 @@ def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): bpy.ops.object.mode_set(mode='OBJECT') - orig_frame = sce.current_frame + orig_frame = sce.frame_current sce.set_frame(PREF_STARTFRAME) - me = ob.create_mesh(True, 'PREVIEW') + me = ob.create_mesh(sce, True, 'PREVIEW') #Flip y and z - mat_flip = Mathutils.Matrix(\ + mat_flip = mathutils.Matrix(\ [1.0, 0.0, 0.0, 0.0],\ [0.0, 0.0, 1.0, 0.0],\ [0.0, 1.0, 0.0, 0.0],\ @@ -113,7 +113,7 @@ def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): """ check_vertcount(me, numverts) - me.transform(mat_flip * ob.matrix) + me.transform(mat_flip * ob.matrix_world) f.write(pack(">%df" % (numverts * 3), *[axis for v in me.verts for axis in v.co])) for frame in range(PREF_STARTFRAME, PREF_ENDFRAME + 1):#in order to start at desired frame @@ -123,9 +123,9 @@ def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): """ sce.set_frame(frame) - me = ob.create_mesh(True, 'PREVIEW') + me = ob.create_mesh(sce, True, 'PREVIEW') check_vertcount(me, numverts) - me.transform(mat_flip * ob.matrix) + me.transform(mat_flip * ob.matrix_world) # Write the vertex data f.write(pack(">%df" % (numverts * 3), *[axis for v in me.verts for axis in v.co])) @@ -159,21 +159,21 @@ class ExportMDD(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for exporting the MDD file", maxlen=1024) + filepath = StringProperty(name="File Path", description="Filepath used for exporting the MDD file", maxlen=1024) check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'}) fps = IntProperty(name="Frames Per Second", description="Number of frames/second", min=minfps, max=maxfps, default=25) - start_frame = IntProperty(name="Start Frame", description="Start frame for baking", min=minframe, max=maxframe, default=1) - end_frame = IntProperty(name="End Frame", description="End frame for baking", min=minframe, max=maxframe, default=250) + frame_start = IntProperty(name="Start Frame", description="Start frame for baking", min=minframe, max=maxframe, default=1) + frame_end = IntProperty(name="End Frame", description="End frame for baking", min=minframe, max=maxframe, default=250) def poll(self, context): ob = context.active_object return (ob and ob.type == 'MESH') def execute(self, context): - if not self.properties.path: + if not self.properties.filepath: raise Exception("filename not set") - write(self.properties.path, context.scene, context.active_object, - self.properties.start_frame, self.properties.end_frame, self.properties.fps) + write(self.properties.filepath, context.scene, context.active_object, + self.properties.frame_start, self.properties.frame_end, self.properties.fps) return {'FINISHED'} def invoke(self, context, event): @@ -183,18 +183,19 @@ class ExportMDD(bpy.types.Operator): def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".mdd") - self.layout.operator(ExportMDD.bl_idname, text="Lightwave Point Cache (.mdd)").path = default_path + import os + default_path = os.path.splitext(bpy.data.filepath)[0] + ".mdd" + self.layout.operator(ExportMDD.bl_idname, text="Lightwave Point Cache (.mdd)").filepath = default_path def register(): bpy.types.register(ExportMDD) bpy.types.INFO_MT_file_export.append(menu_func) + def unregister(): bpy.types.unregister(ExportMDD) bpy.types.INFO_MT_file_export.remove(menu_func) if __name__ == "__main__": register() - diff --git a/release/scripts/io/export_obj.py b/release/scripts/io/export_obj.py index 046ae9ecf99..626b92c3591 100644 --- a/release/scripts/io/export_obj.py +++ b/release/scripts/io/export_obj.py @@ -47,7 +47,7 @@ import time import shutil import bpy -import Mathutils +import mathutils # Returns a tuple - path,extension. @@ -65,19 +65,15 @@ def fixName(name): else: return name.replace(' ', '_') -# A Dict of Materials -# (material.name, image.name):matname_imagename # matname_imagename has gaps removed. -MTL_DICT = {} - -def write_mtl(scene, filename, copy_images): +def write_mtl(scene, filepath, copy_images, mtl_dict): world = scene.world worldAmb = world.ambient_color - dest_dir = os.path.dirname(filename) + dest_dir = os.path.dirname(filepath) def copy_image(image): - fn = bpy.utils.expandpath(image.filename) + fn = bpy.utils.expandpath(image.filepath) fn_strip = os.path.basename(fn) if copy_images: rel = fn_strip @@ -90,12 +86,12 @@ def write_mtl(scene, filename, copy_images): return rel - file = open(filename, "w") + file = open(filepath, "w") # XXX -# file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) - file.write('# Material Count: %i\n' % len(MTL_DICT)) +# file.write('# Blender MTL File: %s\n' % Blender.Get('filepath').split('\\')[-1].split('/')[-1]) + file.write('# Material Count: %i\n' % len(mtl_dict)) # Write material/image combinations we have used. - for key, (mtl_mat_name, mat, img) in MTL_DICT.items(): + for key, (mtl_mat_name, mat, img) in mtl_dict.items(): # Get the Blender data for the material and the image. # Having an image named None will make a bug, dont do it :) @@ -135,15 +131,15 @@ def write_mtl(scene, filename, copy_images): # write relative image path rel = copy_image(img) file.write('map_Kd %s\n' % rel) # Diffuse mapping image -# file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image +# file.write('map_Kd %s\n' % img.filepath.split('\\')[-1].split('/')[-1]) # Diffuse mapping image elif mat: # No face image. if we havea material search for MTex image. for mtex in mat.texture_slots: if mtex and mtex.texture.type == 'IMAGE': try: - filename = copy_image(mtex.texture.image) -# filename = mtex.texture.image.filename.split('\\')[-1].split('/')[-1] - file.write('map_Kd %s\n' % filename) # Diffuse mapping image + filepath = copy_image(mtex.texture.image) +# filepath = mtex.texture.image.filepath.split('\\')[-1].split('/')[-1] + file.write('map_Kd %s\n' % filepath) # Diffuse mapping image break except: # Texture has no image though its an image type, best ignore. @@ -173,7 +169,7 @@ def copy_images(dest_dir): # Get unique image names uniqueImages = {} - for matname, mat, image in MTL_DICT.values(): # Only use image name + for matname, mat, image in mtl_dict.values(): # Only use image name # Get Texface images if image: uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. @@ -193,7 +189,7 @@ def copy_images(dest_dir): copyCount = 0 # for bImage in uniqueImages.values(): -# image_path = bpy.utils.expandpath(bImage.filename) +# image_path = bpy.utils.expandpath(bImage.filepath) # if bpy.sys.exists(image_path): # # Make a name for the target path. # dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] @@ -225,7 +221,7 @@ def write_nurb(file, ob, ob_mat): cu = ob.data # use negative indices - Vector = Blender.Mathutils.Vector + Vector = Blender.mathutils.Vector for nu in cu: if nu.type==0: DEG_ORDER_U = 1 @@ -286,7 +282,7 @@ def write_nurb(file, ob, ob_mat): return tot_verts -def write(filename, objects, scene, +def write(filepath, objects, scene, EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, @@ -355,26 +351,26 @@ def write(filename, objects, scene, return ret - print('OBJ Export path: "%s"' % filename) + print('OBJ Export path: "%s"' % filepath) temp_mesh_name = '~tmp-mesh' time1 = time.clock() # time1 = sys.time() # scn = Scene.GetCurrent() - file = open(filename, "w") + file = open(filepath, "w") # Write Header - file.write('# Blender3D v%s OBJ File: %s\n' % (bpy.app.version_string, bpy.data.filename.split('/')[-1].split('\\')[-1] )) - file.write('# www.blender3d.org\n') + file.write('# Blender v%s OBJ File: %s\n' % (bpy.app.version_string, bpy.data.filepath.split('/')[-1].split('\\')[-1] )) + file.write('# www.blender.org\n') # Tell the obj file what material file to use. if EXPORT_MTL: - mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) - file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) + mtlfilepath = '%s.mtl' % '.'.join(filepath.split('.')[:-1]) + file.write('mtllib %s\n' % ( mtlfilepath.split('\\')[-1].split('/')[-1] )) if EXPORT_ROTX90: - mat_xrot90= Mathutils.RotationMatrix(-math.pi/2, 4, 'X') + mat_xrot90= mathutils.RotationMatrix(-math.pi/2, 4, 'X') # Initialize totals, these are updated each object totverts = totuvco = totno = 1 @@ -383,6 +379,10 @@ def write(filename, objects, scene, globalNormals = {} + # A Dict of Materials + # (material.name, image.name):matname_imagename # matname_imagename has gaps removed. + mtl_dict = {} + # Get all meshes for ob_main in objects: @@ -396,14 +396,14 @@ def write(filename, objects, scene, if ob_main.dupli_type != 'NONE': # XXX print('creating dupli_list on', ob_main.name) - ob_main.create_dupli_list() + ob_main.create_dupli_list(scene) obs = [(dob.object, dob.matrix) for dob in ob_main.dupli_list] # XXX debug print print(ob_main.name, 'has', len(obs), 'dupli children') else: - obs = [(ob_main, ob_main.matrix)] + obs = [(ob_main, ob_main.matrix_world)] for ob, ob_mat in obs: @@ -421,7 +421,7 @@ def write(filename, objects, scene, if ob.type != 'MESH': continue - me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW') + me = ob.create_mesh(scene, EXPORT_APPLY_MODIFIERS, 'PREVIEW') if EXPORT_ROTX90: me.transform(mat_xrot90 * ob_mat) @@ -691,7 +691,7 @@ def write(filename, objects, scene, file.write('usemtl (null)\n') # mat, image else: - mat_data= MTL_DICT.get(key) + mat_data= mtl_dict.get(key) if not mat_data: # First add to global dict so we can export to mtl # Then write mtl @@ -701,9 +701,9 @@ def write(filename, objects, scene, # If none image dont bother adding it to the name if key[1] == None: - mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image + mat_data = mtl_dict[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image else: - mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image + mat_data = mtl_dict[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image if EXPORT_GROUP_BY_MAT: file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.data.name), mat_data[0]) ) # can be mat_image or (null) @@ -782,22 +782,22 @@ def write(filename, objects, scene, # Now we have all our materials, save them if EXPORT_MTL: - write_mtl(scene, mtlfilename, EXPORT_COPY_IMAGES) + write_mtl(scene, mtlfilepath, EXPORT_COPY_IMAGES, mtl_dict) # if EXPORT_COPY_IMAGES: -# dest_dir = os.path.basename(filename) -# # dest_dir = filename +# dest_dir = os.path.basename(filepath) +# # dest_dir = filepath # # # Remove chars until we are just the path. # # while dest_dir and dest_dir[-1] not in '\\/': # # dest_dir = dest_dir[:-1] # if dest_dir: -# copy_images(dest_dir) +# copy_images(dest_dir, mtl_dict) # else: -# print('\tError: "%s" could not be used as a base for an image path.' % filename) +# print('\tError: "%s" could not be used as a base for an image path.' % filepath) print("OBJ Export time: %.2f" % (time.clock() - time1)) # print "OBJ Export time: %.2f" % (sys.time() - time1) -def do_export(filename, context, +def do_export(filepath, context, EXPORT_APPLY_MODIFIERS = True, # not used EXPORT_ROTX90 = True, # wrong EXPORT_TRI = False, # ok @@ -816,14 +816,15 @@ def do_export(filename, context, EXPORT_KEEP_VERT_ORDER = False, EXPORT_POLYGROUPS = False, EXPORT_CURVE_AS_NURBS = True): - # Window.EditMode(0) - # Window.WaitCursor(1) - - base_name, ext = splitExt(filename) + + base_name, ext = splitExt(filepath) context_name = [base_name, '', '', ext] # Base name, scene name, frame number, extension orig_scene = context.scene + # Exit edit mode before exporting, so current object states are exported properly. + bpy.ops.object.mode_set(mode='OBJECT') + # if EXPORT_ALL_SCENES: # export_scenes = bpy.data.scenes # else: @@ -839,23 +840,23 @@ def do_export(filename, context, for scn in export_scenes: # scn.makeCurrent() # If already current, this is not slow. # context = scn.getRenderingContext() - orig_frame = scn.current_frame + orig_frame = scn.frame_current if EXPORT_ALL_SCENES: # Add scene name into the context_name context_name[1] = '_%s' % bpy.utils.clean_name(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. # Export an animation? if EXPORT_ANIMATION: - scene_frames = range(scn.start_frame, context.end_frame+1) # Up to and including the end frame. + scene_frames = range(scn.frame_start, context.frame_end + 1) # Up to and including the end frame. else: scene_frames = [orig_frame] # Dont export an animation. # Loop through all frames in the scene and export. for frame in scene_frames: - if EXPORT_ANIMATION: # Add frame to the filename. + if EXPORT_ANIMATION: # Add frame to the filepath. context_name[2] = '_%.6d' % frame - scn.current_frame = frame + scn.frame_current = frame if EXPORT_SEL_ONLY: export_objects = context.selected_objects else: @@ -874,7 +875,7 @@ def do_export(filename, context, EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS) - scn.current_frame = orig_frame + scn.frame_current = orig_frame # Restore old active scene. # orig_scene.makeCurrent() @@ -899,7 +900,7 @@ class ExportOBJ(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for exporting the OBJ file", maxlen= 1024, default= "") + filepath = StringProperty(name="File Path", description="Filepath used for exporting the OBJ file", maxlen= 1024, default= "") check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'}) # context group @@ -931,11 +932,11 @@ class ExportOBJ(bpy.types.Operator): def execute(self, context): - path = self.properties.path - if not path.lower().endswith(".obj"): - path += ".obj" + filepath = self.properties.filepath + if not filepath.lower().endswith(".obj"): + filepath += ".obj" - do_export(path, context, + do_export(filepath, context, EXPORT_TRI=self.properties.use_triangles, EXPORT_EDGES=self.properties.use_edges, EXPORT_NORMALS=self.properties.use_normals, @@ -963,8 +964,8 @@ class ExportOBJ(bpy.types.Operator): def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".obj") - self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)").path = default_path + default_path = os.path.splitext(bpy.data.filepath)[0] + ".obj" + self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)").filepath = default_path def register(): diff --git a/release/scripts/io/export_ply.py b/release/scripts/io/export_ply.py index 92d4f2ff914..0b936bdceb2 100644 --- a/release/scripts/io/export_ply.py +++ b/release/scripts/io/export_ply.py @@ -99,10 +99,11 @@ def write(filename, scene, ob, \ Window.WaitCursor(1) """ + bpy.ops.object.mode_set(mode='OBJECT') #mesh = BPyMesh.getMeshFromObject(ob, None, EXPORT_APPLY_MODIFIERS, False, scn) # XXX if EXPORT_APPLY_MODIFIERS: - mesh = ob.create_mesh(True, 'PREVIEW') + mesh = ob.create_mesh(scene, True, 'PREVIEW') else: mesh = ob.data @@ -110,7 +111,7 @@ def write(filename, scene, ob, \ raise ("Error, could not get mesh data from active object") return - # mesh.transform(ob.matrixWorld) # XXX + # mesh.transform(ob.matrix_world) # XXX faceUV = (len(mesh.uv_textures) > 0) vertexUV = (len(mesh.sticky) > 0) @@ -202,7 +203,7 @@ def write(filename, scene, ob, \ file.write('ply\n') file.write('format ascii 1.0\n') - file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (bpy.app.version_string, bpy.data.filename.split('/')[-1].split('\\')[-1])) + file.write('comment Created by Blender %s - www.blender.org, source file: %s\n' % (bpy.app.version_string, bpy.data.filepath.split('/')[-1].split('\\')[-1])) file.write('element vertex %d\n' % len(ply_verts)) @@ -210,13 +211,10 @@ def write(filename, scene, ob, \ file.write('property float y\n') file.write('property float z\n') - # XXX - """ if EXPORT_NORMALS: file.write('property float nx\n') file.write('property float ny\n') file.write('property float nz\n') - """ if EXPORT_UV: file.write('property float s\n') file.write('property float t\n') @@ -231,10 +229,8 @@ def write(filename, scene, ob, \ for i, v in enumerate(ply_verts): file.write('%.6f %.6f %.6f ' % tuple(mesh_verts[v[0]].co)) # co - """ if EXPORT_NORMALS: file.write('%.6f %.6f %.6f ' % v[1]) # no - """ if EXPORT_UV: file.write('%.6f %.6f ' % v[2]) # uv if EXPORT_COLORS: @@ -271,7 +267,7 @@ class ExportPLY(bpy.types.Operator): # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for exporting the PLY file", maxlen=1024, default="") + filepath = StringProperty(name="File Path", description="Filepath used for exporting the PLY file", maxlen=1024, default="") check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'}) use_modifiers = BoolProperty(name="Apply Modifiers", description="Apply Modifiers to the exported mesh", default=True) use_normals = BoolProperty(name="Normals", description="Export Normals for smooth and hard shaded faces", default=True) @@ -284,10 +280,10 @@ class ExportPLY(bpy.types.Operator): def execute(self, context): # print("Selected: " + context.active_object.name) - if not self.properties.path: + if not self.properties.filepath: raise Exception("filename not set") - write(self.properties.path, context.scene, context.active_object,\ + write(self.properties.filepath, context.scene, context.active_object,\ EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers, EXPORT_NORMALS=self.properties.use_normals, EXPORT_UV=self.properties.use_uvs, @@ -314,8 +310,9 @@ class ExportPLY(bpy.types.Operator): def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".ply") - self.layout.operator(ExportPLY.bl_idname, text="Stanford (.ply)").path = default_path + import os + default_path = os.path.splitext(bpy.data.filepath)[0] + ".ply" + self.layout.operator(ExportPLY.bl_idname, text="Stanford (.ply)").filepath = default_path def register(): diff --git a/release/scripts/io/export_x3d.py b/release/scripts/io/export_x3d.py index f1ab8cd3de7..1bad80f6d8e 100644 --- a/release/scripts/io/export_x3d.py +++ b/release/scripts/io/export_x3d.py @@ -69,7 +69,7 @@ import math import os import bpy -import Mathutils +import mathutils from export_3ds import create_derived_objects, free_derived_objects @@ -81,7 +81,7 @@ from export_3ds import create_derived_objects, free_derived_objects # DEG2RAD=0.017453292519943295 -MATWORLD= Mathutils.RotationMatrix(-90, 4, 'X') +MATWORLD= mathutils.RotationMatrix(-90, 4, 'X') #################################### # Global Variables @@ -237,7 +237,7 @@ class x3d_class: lens = min(lens, math.pi) # get the camera location, subtract 90 degress from X to orient like X3D does - # mat = ob.matrixWorld - mat is now passed! + # mat = ob.matrix_world - mat is now passed! loc = self.rotatePointForVRML(mat.translation_part()) rot = mat.to_euler() @@ -300,7 +300,7 @@ class x3d_class: # note -dz seems to equal om[3][1] # note dy seems to equal om[3][2] - #location=(ob.matrixWorld*MATWORLD).translation_part() # now passed + #location=(ob.matrix_world*MATWORLD).translation_part() # now passed location=(mtx*MATWORLD).translation_part() radius = lamp.distance*math.cos(beamWidth) @@ -346,7 +346,7 @@ class x3d_class: ambi = 0 ambientIntensity = 0 - # location=(ob.matrixWorld*MATWORLD).translation_part() # now passed + # location=(ob.matrix_world*MATWORLD).translation_part() # now passed location= (mtx*MATWORLD).translation_part() self.file.write("<PointLight DEF=\"%s\" " % safeName) @@ -364,7 +364,7 @@ class x3d_class: return else: dx,dy,dz = self.computeDirection(mtx) - # location=(ob.matrixWorld*MATWORLD).translation_part() + # location=(ob.matrix_world*MATWORLD).translation_part() location=(mtx*MATWORLD).translation_part() self.writeIndented("<%s\n" % obname,1) self.writeIndented("direction=\"%s %s %s\"\n" % (round(dx,3),round(dy,3),round(dz,3))) @@ -445,7 +445,7 @@ class x3d_class: else: bTwoSided=0 - # mtx = ob.matrixWorld * MATWORLD # mtx is now passed + # mtx = ob.matrix_world * MATWORLD # mtx is now passed mtx = mtx * MATWORLD loc= mtx.translation_part() @@ -601,7 +601,7 @@ class x3d_class: self.file.write("\">\n") else: #-- vertices - # mesh.transform(ob.matrixWorld) + # mesh.transform(ob.matrix_world) self.writeIndented("<Coordinate DEF=\"%s%s\" \n" % ("coord_",meshName), 1) self.file.write("\t\t\t\tpoint=\"") for v in mesh.verts: @@ -717,7 +717,7 @@ class x3d_class: def writeImageTexture(self, image): name = image.name - filename = image.filename.split('/')[-1].split('\\')[-1] + filename = image.filepath.split('/')[-1].split('\\')[-1] if name in self.texNames: self.writeIndented("<ImageTexture USE=\"%s\" />\n" % self.cleanStr(name)) self.texNames[name] += 1 @@ -794,28 +794,28 @@ class x3d_class: pic = tex.image # using .expandpath just in case, os.path may not expect // - basename = os.path.basename(bpy.utils.expandpath(pic.filename)) + basename = os.path.basename(bpy.utils.expandpath(pic.filepath)) pic = alltextures[i].image # pic = alltextures[i].getImage() if (namemat == "back") and (pic != None): self.file.write("\n\tbackUrl=\"%s\" " % basename) - # self.file.write("\n\tbackUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + # self.file.write("\n\tbackUrl=\"%s\" " % pic.filepath.split('/')[-1].split('\\')[-1]) elif (namemat == "bottom") and (pic != None): self.writeIndented("bottomUrl=\"%s\" " % basename) - # self.writeIndented("bottomUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + # self.writeIndented("bottomUrl=\"%s\" " % pic.filepath.split('/')[-1].split('\\')[-1]) elif (namemat == "front") and (pic != None): self.writeIndented("frontUrl=\"%s\" " % basename) - # self.writeIndented("frontUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + # self.writeIndented("frontUrl=\"%s\" " % pic.filepath.split('/')[-1].split('\\')[-1]) elif (namemat == "left") and (pic != None): self.writeIndented("leftUrl=\"%s\" " % basename) - # self.writeIndented("leftUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + # self.writeIndented("leftUrl=\"%s\" " % pic.filepath.split('/')[-1].split('\\')[-1]) elif (namemat == "right") and (pic != None): self.writeIndented("rightUrl=\"%s\" " % basename) - # self.writeIndented("rightUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + # self.writeIndented("rightUrl=\"%s\" " % pic.filepath.split('/')[-1].split('\\')[-1]) elif (namemat == "top") and (pic != None): self.writeIndented("topUrl=\"%s\" " % basename) - # self.writeIndented("topUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1]) + # self.writeIndented("topUrl=\"%s\" " % pic.filepath.split('/')[-1].split('\\')[-1]) self.writeIndented("/>\n\n") ########################################################## @@ -852,10 +852,10 @@ class x3d_class: # -------------------------- - for ob_main in [o for o in scene.objects if o.is_visible()]: + for ob_main in [o for o in scene.objects if o.is_visible(scene)]: # for ob_main in scene.objects.context: - free, derived = create_derived_objects(ob_main) + free, derived = create_derived_objects(scene, ob_main) if derived == None: continue @@ -871,7 +871,7 @@ class x3d_class: # elif objType in ("Mesh", "Curve", "Surf", "Text") : if EXPORT_APPLY_MODIFIERS or objType != 'MESH': # if EXPORT_APPLY_MODIFIERS or objType != 'Mesh': - me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW') + me = ob.create_mesh(scene, EXPORT_APPLY_MODIFIERS, 'PREVIEW') # me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene) else: me = ob.data @@ -1155,8 +1155,9 @@ def x3d_export(filename, scene = context.scene - # scene = Blender.Scene.GetCurrent() world = scene.world + + bpy.ops.object.mode_set(mode='OBJECT') # XXX these are global textures while .Get() returned only scene's? alltextures = bpy.data.textures @@ -1214,9 +1215,6 @@ def x3d_export_ui(filename): ######################################################### -# if __name__ == '__main__': -# Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d')) - from bpy.props import * class ExportX3D(bpy.types.Operator): @@ -1226,7 +1224,7 @@ class ExportX3D(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for exporting the X3D file", maxlen= 1024, default= "") + filepath = StringProperty(name="File Path", description="Filepath used for exporting the X3D file", maxlen= 1024, default= "") check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'}) apply_modifiers = BoolProperty(name="Apply Modifiers", description="Use transformed mesh data from each object", default=True) @@ -1234,7 +1232,7 @@ class ExportX3D(bpy.types.Operator): compress = BoolProperty(name="Compress", description="GZip the resulting file, requires a full python install", default=False) def execute(self, context): - x3d_export(self.properties.path, context, self.properties.apply_modifiers, self.properties.triangulate, self.properties.compress) + x3d_export(self.properties.filepath, context, self.properties.apply_modifiers, self.properties.triangulate, self.properties.compress) return {'FINISHED'} def invoke(self, context, event): @@ -1244,8 +1242,8 @@ class ExportX3D(bpy.types.Operator): def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".x3d") - self.layout.operator(ExportX3D.bl_idname, text="X3D Extensible 3D (.x3d)").path = default_path + default_path = os.path.splitext(bpy.data.filepath)[0] + ".x3d" + self.layout.operator(ExportX3D.bl_idname, text="X3D Extensible 3D (.x3d)").filepath = default_path def register(): @@ -1261,4 +1259,3 @@ def unregister(): if __name__ == "__main__": register() - diff --git a/release/scripts/io/import_anim_bvh.py b/release/scripts/io/import_anim_bvh.py index 364bd6e96ee..d497ac47065 100644 --- a/release/scripts/io/import_anim_bvh.py +++ b/release/scripts/io/import_anim_bvh.py @@ -22,8 +22,8 @@ import math from math import radians import bpy -import Mathutils -from Mathutils import Vector, Euler, Matrix, RotationMatrix, TranslationMatrix +import mathutils +from mathutils import Vector, Euler, Matrix, RotationMatrix, TranslationMatrix class bvh_node_class(object): @@ -83,7 +83,7 @@ def eulerRotate(x, y, z, rot_order): # Should work but doesnt! ''' - eul = Euler(x,y,z) + eul = Euler((x, y, z)) eul.order = "XYZ"[rot_order[0]] + "XYZ"[rot_order[1]] + "XYZ"[rot_order[2]] return tuple(eul.to_matrix().to_euler()) ''' @@ -136,7 +136,7 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0): #print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1]) lineIdx += 2 # Incriment to the next line (Offset) - rest_head_local = Vector(float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3])) * GLOBAL_SCALE + rest_head_local = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * GLOBAL_SCALE lineIdx += 1 # Incriment to the next line (Channels) # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation] @@ -188,7 +188,7 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0): # Account for an end node if file_lines[lineIdx][0].lower() == 'end' and file_lines[lineIdx][1].lower() == 'site': # There is somtimes a name after 'End Site' but we will ignore it. lineIdx += 2 # Incriment to the next line (Offset) - rest_tail = Vector(float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3])) * GLOBAL_SCALE + rest_tail = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * GLOBAL_SCALE bvh_nodes_serial[-1].rest_tail_world = bvh_nodes_serial[-1].rest_head_world + rest_tail bvh_nodes_serial[-1].rest_tail_local = bvh_nodes_serial[-1].rest_head_local + rest_tail @@ -267,8 +267,8 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0): # raise 'error, bvh node has no end and no children. bad file' # Removed temp for now - rest_tail_world = Vector(0.0, 0.0, 0.0) - rest_tail_local = Vector(0.0, 0.0, 0.0) + rest_tail_world = Vector((0.0, 0.0, 0.0)) + rest_tail_local = Vector((0.0, 0.0, 0.0)) for bvh_node_child in bvh_node.children: rest_tail_world += bvh_node_child.rest_head_world rest_tail_local += bvh_node_child.rest_head_local @@ -321,14 +321,14 @@ def bvh_node_dict2objects(context, bvh_nodes, IMPORT_START_FRAME=1, IMPORT_LOOP= # Animate the data, the last used bvh_node will do since they all have the same number of frames - for current_frame in range(len(bvh_node.anim_data)): - Blender.Set('curframe', current_frame + IMPORT_START_FRAME) + for frame_current in range(len(bvh_node.anim_data)): + Blender.Set('curframe', frame_current + IMPORT_START_FRAME) for bvh_node in bvh_nodes.values(): - lx, ly, lz, rx, ry, rz = bvh_node.anim_data[current_frame] + lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current] rest_head_local = bvh_node.rest_head_local - bvh_node.temp.loc = rest_head_local + Vector(lx, ly, lz) + bvh_node.temp.loc = rest_head_local + Vector((lx, ly, lz)) bvh_node.temp.rot = rx, ry, rz @@ -458,13 +458,11 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM pose_bone = pose_bones[bone_name] pose_bone.rotation_mode = eul_order_lookup[tuple(bvh_node.rot_order)] - elif ROT_MODE == 'XYZ': - print(2) + elif ROT_MODE != 'QUATERNION': for pose_bone in pose_bones: - pose_bone.rotation_mode = 'XYZ' + pose_bone.rotation_mode = ROT_MODE else: # Quats default - print(3) pass context.scene.update() @@ -508,30 +506,30 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM prev_euler = [Euler() for i in range(len(bvh_nodes))] # Animate the data, the last used bvh_node will do since they all have the same number of frames - for current_frame in range(len(bvh_node.anim_data)-1): # skip the first frame (rest frame) - # print current_frame + for frame_current in range(len(bvh_node.anim_data)-1): # skip the first frame (rest frame) + # print frame_current - # if current_frame==40: # debugging + # if frame_current==40: # debugging # break # Dont neet to set the current frame for i, bvh_node in enumerate(bvh_nodes.values()): pose_bone, bone, bone_rest_matrix, bone_rest_matrix_inv = bvh_node.temp - lx, ly, lz, rx, ry, rz = bvh_node.anim_data[current_frame + 1] + lx, ly, lz, rx, ry, rz = bvh_node.anim_data[frame_current + 1] if bvh_node.has_rot: - bone_rotation_matrix = Euler(rx, ry, rz).to_matrix().resize4x4() + bone_rotation_matrix = Euler((rx, ry, rz)).to_matrix().resize4x4() bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix if ROT_MODE == 'QUATERNION': pose_bone.rotation_quaternion = bone_rotation_matrix.to_quat() else: - euler = bone_rotation_matrix.to_euler('XYZ', prev_euler[i]) # pose_bone.rotation_mode # TODO, XYZ default for now + euler = bone_rotation_matrix.to_euler(pose_bone.rotation_mode, prev_euler[i]) pose_bone.rotation_euler = euler prev_euler[i] = euler if bvh_node.has_loc: - pose_bone.location = (bone_rest_matrix_inv * TranslationMatrix(Vector(lx, ly, lz) - bvh_node.rest_head_local)).translation_part() + pose_bone.location = (bone_rest_matrix_inv * TranslationMatrix(Vector((lx, ly, lz)) - bvh_node.rest_head_local)).translation_part() if bvh_node.has_loc: pose_bone.keyframe_insert("location") @@ -563,23 +561,23 @@ class BvhImporter(bpy.types.Operator): bl_idname = "import_anim.bvh" bl_label = "Import BVH" - path = StringProperty(name="File Path", description="File path used for importing the OBJ file", maxlen=1024, default="") + filepath = StringProperty(name="File Path", description="Filepath used for importing the OBJ file", maxlen=1024, default="") scale = FloatProperty(name="Scale", description="Scale the BVH by this value", min=0.0001, max=1000000.0, soft_min=0.001, soft_max=100.0, default=0.1) - start_frame = IntProperty(name="Start Frame", description="Starting frame for the animation", default=1) + frame_start = IntProperty(name="Start Frame", description="Starting frame for the animation", default=1) loop = BoolProperty(name="Loop", description="Loop the animation playback", default=False) rotate_mode = EnumProperty(items=( ('QUATERNION', "Quaternion", "Convert rotations to quaternions"), - # ('NATIVE', "Euler (Native)", "Use the rotation order defined in the BVH file"), + ('NATIVE', "Euler (Native)", "Use the rotation order defined in the BVH file"), ('XYZ', "Euler (XYZ)", "Convert rotations to euler XYZ"), - # ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"), - # ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"), - # ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"), - # ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"), - # ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX")), + ('XZY', "Euler (XZY)", "Convert rotations to euler XZY"), + ('YXZ', "Euler (YXZ)", "Convert rotations to euler YXZ"), + ('YZX', "Euler (YZX)", "Convert rotations to euler YZX"), + ('ZXY', "Euler (ZXY)", "Convert rotations to euler ZXY"), + ('ZYX', "Euler (ZYX)", "Convert rotations to euler ZYX"), ), name="Rotation", description="Rotation conversion.", - default='QUATERNION') + default='NATIVE') def execute(self, context): # print("Selected: " + context.active_object.name) @@ -587,7 +585,7 @@ class BvhImporter(bpy.types.Operator): t1 = time.time() print('\tparsing bvh...', end="") - bvh_nodes = read_bvh(context, self.properties.path, + bvh_nodes = read_bvh(context, self.properties.filepath, ROT_MODE=self.properties.rotate_mode, GLOBAL_SCALE=self.properties.scale) @@ -597,7 +595,7 @@ class BvhImporter(bpy.types.Operator): bvh_node_dict2armature(context, bvh_nodes, ROT_MODE=self.properties.rotate_mode, - IMPORT_START_FRAME=self.properties.start_frame, + IMPORT_START_FRAME=self.properties.frame_start, IMPORT_LOOP=self.properties.loop) print('Done in %.4f\n' % (time.time() - t1)) @@ -609,7 +607,8 @@ class BvhImporter(bpy.types.Operator): return {'RUNNING_MODAL'} -menu_func = lambda self, context: self.layout.operator(BvhImporter.bl_idname, text="Motion Capture (.bvh)") +def menu_func(self, context): + self.layout.operator(BvhImporter.bl_idname, text="Motion Capture (.bvh)") def register(): diff --git a/release/scripts/io/import_scene_3ds.py b/release/scripts/io/import_scene_3ds.py index d1dbdc62563..7b3004d4f52 100644 --- a/release/scripts/io/import_scene_3ds.py +++ b/release/scripts/io/import_scene_3ds.py @@ -144,7 +144,7 @@ import struct from import_scene_obj import unpack_face_list, load_image import bpy -import Mathutils +import mathutils BOUNDS_3DS = [] @@ -310,8 +310,8 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): contextObName = None contextLamp = [None, None] # object, Data contextMaterial = None - contextMatrix_rot = None # Blender.Mathutils.Matrix(); contextMatrix.identity() - #contextMatrix_tx = None # Blender.Mathutils.Matrix(); contextMatrix.identity() + contextMatrix_rot = None # Blender.mathutils.Matrix(); contextMatrix.identity() + #contextMatrix_tx = None # Blender.mathutils.Matrix(); contextMatrix.identity() contextMesh_vertls = None contextMesh_facels = None contextMeshMaterials = {} # matname:[face_idxs] @@ -360,7 +360,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): vertMappingIndex = 0 vertsToUse = [i for i in range(len(myContextMesh_vertls)) if faceVertUsers[i]] - myVertMapping = dict( [ (ii, i) for i, ii in enumerate(vertsToUse) ] ) + myVertMapping = {ii: i for i, ii in enumerate(vertsToUse)} tempName= '%s_%s' % (contextObName, matName) # matName may be None. bmesh = bpy.data.meshes.new(tempName) @@ -427,9 +427,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' if contextMatrix_rot: - # ob.matrix = [x for row in contextMatrix_rot for x in row] - ob.matrix = contextMatrix_rot -# ob.setMatrix(contextMatrix_rot) + ob.matrix_world = contextMatrix_rot importedObjects.append(ob) bmesh.update() @@ -722,7 +720,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): def getuv(): temp_data = file.read(STRUCT_SIZE_2FLOAT) new_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each - return Mathutils.Vector( struct.unpack('<2f', temp_data) ) + return mathutils.Vector( struct.unpack('<2f', temp_data) ) contextMeshUV = [ getuv() for i in range(num_uv) ] @@ -732,7 +730,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): data = list( struct.unpack('<ffffffffffff', temp_data) ) new_chunk.bytes_read += STRUCT_SIZE_4x3MAT - contextMatrix_rot = Mathutils.Matrix(\ + contextMatrix_rot = mathutils.Matrix(\ data[:3] + [0],\ data[3:6] + [0],\ data[6:9] + [0],\ @@ -740,7 +738,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' - contextMatrix_rot = Blender.Mathutils.Matrix(\ + contextMatrix_rot = Blender.mathutils.Matrix(\ data[:3] + [0],\ data[3:6] + [0],\ data[6:9] + [0],\ @@ -748,14 +746,14 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): ''' ''' - contextMatrix_rot = Blender.Mathutils.Matrix(\ + contextMatrix_rot = Blender.mathutils.Matrix(\ data[:3] ,\ data[3:6],\ data[6:9]) ''' ''' - contextMatrix_rot = Blender.Mathutils.Matrix() + contextMatrix_rot = Blender.mathutils.Matrix() m = 0 for j in xrange(4): for i in xrange(3): @@ -773,7 +771,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): #print contextMatrix_rot contextMatrix_rot.invert() #print contextMatrix_rot - #contextMatrix_tx = Blender.Mathutils.TranslationMatrix(0.5 * Blender.Mathutils.Vector(data[9:])) + #contextMatrix_tx = Blender.mathutils.TranslationMatrix(0.5 * Blender.mathutils.Vector(data[9:])) #contextMatrix_tx.invert() #tx.invert() @@ -892,7 +890,7 @@ def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, # me = ob.getData(mesh=1) # me.verts.delete([me.verts[0],]) # if not APPLY_MATRIX: -# me.transform(ob.matrixWorld.copy().invert()) +# me.transform(ob.matrix_world.copy().invert()) # Done DUMMYVERT """ @@ -946,11 +944,11 @@ def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, SCALE/=10 # SCALE Matrix - SCALE_MAT = Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) -# SCALE_MAT = Blender.Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) + SCALE_MAT = mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) +# SCALE_MAT = Blender.mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) for ob in importedObjects: - ob.setMatrix(ob.matrixWorld * SCALE_MAT) + ob.matrix_world = ob.matrix_world * SCALE_MAT # Done constraining to bounds. @@ -1012,16 +1010,14 @@ class IMPORT_OT_autodesk_3ds(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for importing the 3DS file", maxlen= 1024, default= "") - filename = StringProperty(name="File Name", description="Name of the file.") - directory = StringProperty(name="Directory", description="Directory of the file.") + filepath = StringProperty(name="File Path", description="Filepath used for importing the 3DS file", maxlen= 1024, default= "") # size_constraint = FloatProperty(name="Size Constraint", description="Scale the model by 10 until it reacehs the size constraint. Zero Disables.", min=0.0, max=1000.0, soft_min=0.0, soft_max=1000.0, default=10.0), # search_images = BoolProperty(name="Image Search", description="Search subdirectories for any assosiated images (Warning, may be slow)", default=True), # apply_matrix = BoolProperty(name="Transform Fix", description="Workaround for object transformations importing incorrectly", default=False), def execute(self, context): - load_3ds(self.properties.path, context, 0.0, False, False) + load_3ds(self.properties.filepath, context, 0.0, False, False) return {'FINISHED'} def invoke(self, context, event): @@ -1030,8 +1026,8 @@ class IMPORT_OT_autodesk_3ds(bpy.types.Operator): return {'RUNNING_MODAL'} -menu_func = lambda self, context: self.layout.operator(IMPORT_OT_autodesk_3ds.bl_idname, text="3D Studio (.3ds)") - +def menu_func(self, context): + self.layout.operator(IMPORT_OT_autodesk_3ds.bl_idname, text="3D Studio (.3ds)") def register(): bpy.types.register(IMPORT_OT_autodesk_3ds) diff --git a/release/scripts/io/import_scene_obj.py b/release/scripts/io/import_scene_obj.py index c5bd8339a15..afe82410557 100644 --- a/release/scripts/io/import_scene_obj.py +++ b/release/scripts/io/import_scene_obj.py @@ -54,8 +54,8 @@ Note, This loads mesh objects and materials only, nurbs and curves are not suppo import os import time import bpy -import Mathutils -import Geometry +import mathutils +from geometry import PolyFill # from Blender import Mesh, Draw, Window, Texture, Material, sys # # import BPyMesh @@ -65,19 +65,6 @@ import Geometry # try: import os # except: os= False -# Generic path functions -def stripFile(path): - '''Return directory, where the file is''' - lastSlash= max(path.rfind('\\'), path.rfind('/')) - if lastSlash != -1: - path= path[:lastSlash] - return '%s%s' % (path, os.sep) -# return '%s%s' % (path, sys.sep) - -def stripPath(path): - '''Strips the slashes from the back of a string''' - return path.split('/')[-1].split('\\')[-1] - def stripExt(name): # name is a string '''Strips the prefix off the name before writing''' index= name.rfind('.') @@ -127,7 +114,7 @@ def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS= True): if not set: # Need sets for this, otherwise do a normal fill. PREF_FIX_LOOPS= False - Vector= Mathutils.Vector + Vector= mathutils.Vector if not indices: return [] @@ -158,7 +145,7 @@ def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS= True): if verts[i][1]==verts[i-1][0]: verts.pop(i-1) - fill= Geometry.PolyFill([verts]) + fill= PolyFill([verts]) else: ''' @@ -266,7 +253,7 @@ def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS= True): vert_map[i+ii]= vert[2] ii+=len(verts) - fill= Geometry.PolyFill([ [v[0] for v in loop] for loop in loop_list ]) + fill= PolyFill([ [v[0] for v in loop] for loop in loop_list ]) #draw_loops(loop_list) #raise 'done loop' # map to original indicies @@ -316,13 +303,13 @@ def line_value(line_split): def load_image(imagepath, dirname): if os.path.exists(imagepath): - return bpy.data.add_image(imagepath) + return bpy.data.images.load(imagepath) variants = [os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] for path in variants: if os.path.exists(path): - return bpy.data.add_image(path) + return bpy.data.images.load(path) else: print(path, "doesn't exist") @@ -360,7 +347,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ Create all the used materials in this obj, assign colors and images to the materials from all referenced material libs ''' - DIR= stripFile(filepath) + DIR= os.path.dirname(filepath) #==================================================================================# # This function sets textures defined in .mtl file # @@ -431,7 +418,7 @@ def create_materials(filepath, material_libs, unique_materials, unique_material_ # Add an MTL with the same name as the obj if no MTLs are spesified. - temp_mtl= stripExt(stripPath(filepath))+ '.mtl' + temp_mtl = os.path.splitext((os.path.basename(filepath)))[0] + '.mtl' if os.path.exists(DIR + temp_mtl) and temp_mtl not in material_libs: # if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs: @@ -521,10 +508,9 @@ def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, ''' Takes vert_loc and faces, and seperates into multiple sets of (verts_loc, faces, unique_materials, dataname) - This is done so objects do not overload the 16 material limit. ''' - filename = stripExt(stripPath(filepath)) + filename = os.path.splitext((os.path.basename(filepath)))[0] if not SPLIT_OB_OR_GROUP and not SPLIT_MATERIALS: # use the filename for the object name since we arnt chopping up the mesh. @@ -610,7 +596,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if unique_smooth_groups: sharp_edges= {} - smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in list(unique_smooth_groups.keys()) ]) + smooth_group_users = {context_smooth_group: {} for context_smooth_group in list(unique_smooth_groups.keys())} context_smooth_group_old= -1 # Split fgons into tri's @@ -705,7 +691,7 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l # map the material names to an index - material_mapping= dict([(name, i) for i, name in enumerate(unique_materials)]) # enumerate over unique_materials keys() + material_mapping = {name: i for i, name in enumerate(unique_materials)} # enumerate over unique_materials keys() materials= [None] * len(unique_materials) @@ -715,9 +701,8 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l me= bpy.data.meshes.new(dataname) # make sure the list isnt too big - for material in materials[0:16]: + for material in materials: me.add_material(material) -# me.materials= materials[0:16] # make sure the list isnt too big. #me.verts.extend([(0,0,0)]) # dummy vert me.add_geometry(len(verts_loc), 0, len(faces)) @@ -772,8 +757,6 @@ def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_l if context_material: if context_material_old is not context_material: mat= material_mapping[context_material] - if mat>15: - mat= 15 context_material_old= context_material blender_face.material_index= mat @@ -1363,7 +1346,7 @@ def load_obj_ui(filepath, BATCH_LOAD= False): 'Separate objects from obj...',\ ('Object', SPLIT_OBJECTS, 'Import OBJ Objects into Blender Objects'),\ ('Group', SPLIT_GROUPS, 'Import OBJ Groups into Blender Objects'),\ - ('Material', SPLIT_MATERIALS, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)'),\ + ('Split Materials', SPLIT_MATERIALS, 'Import each material into a seperate mesh'),\ 'Options...',\ ('Keep Vert Order', KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\ ('Clamp Scale:', CLAMP_SIZE, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)'),\ @@ -1449,7 +1432,7 @@ def load_obj_ui(filepath, BATCH_LOAD= False): Draw.BeginAlign() SPLIT_OBJECTS = Draw.Toggle('Object', EVENT_REDRAW, ui_x+9, ui_y+89, 55, 21, SPLIT_OBJECTS.val, 'Import OBJ Objects into Blender Objects', do_split) SPLIT_GROUPS = Draw.Toggle('Group', EVENT_REDRAW, ui_x+64, ui_y+89, 55, 21, SPLIT_GROUPS.val, 'Import OBJ Groups into Blender Objects', do_split) - SPLIT_MATERIALS = Draw.Toggle('Material', EVENT_REDRAW, ui_x+119, ui_y+89, 60, 21, SPLIT_MATERIALS.val, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)', do_split) + SPLIT_MATERIALS = Draw.Toggle('Split Materials', EVENT_REDRAW, ui_x+119, ui_y+89, 60, 21, SPLIT_MATERIALS.val, 'Import each material into a seperate mesh', do_split) Draw.EndAlign() # Only used for user feedback @@ -1580,14 +1563,14 @@ class IMPORT_OT_obj(bpy.types.Operator): # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for importing the OBJ file", maxlen= 1024, default= "") + filepath = StringProperty(name="File Path", description="Filepath used for importing the OBJ file", maxlen= 1024, default= "") CREATE_SMOOTH_GROUPS = BoolProperty(name="Smooth Groups", description="Surround smooth groups by sharp edges", default= True) CREATE_FGONS = BoolProperty(name="NGons as FGons", description="Import faces with more then 4 verts as fgons", default= True) CREATE_EDGES = BoolProperty(name="Lines as Edges", description="Import lines and faces with 2 verts as edge", default= True) SPLIT_OBJECTS = BoolProperty(name="Object", description="Import OBJ Objects into Blender Objects", default= True) SPLIT_GROUPS = BoolProperty(name="Group", description="Import OBJ Groups into Blender Objects", default= True) - SPLIT_MATERIALS = BoolProperty(name="Material", description="Import each material into a seperate mesh (Avoids > 16 per mesh error)", default= True) + SPLIT_MATERIALS = BoolProperty(name="Split Materials", description="Import each material into a seperate mesh", default= False) # old comment: only used for user feedback # disabled this option because in old code a handler for it disabled SPLIT* params, it's not passed to load_obj # KEEP_VERT_ORDER = BoolProperty(name="Keep Vert Order", description="Keep vert and face order, disables split options, enable for morph targets", default= True) @@ -1600,7 +1583,7 @@ class IMPORT_OT_obj(bpy.types.Operator): def execute(self, context): # print("Selected: " + context.active_object.name) - load_obj(self.properties.path, + load_obj(self.properties.filepath, context, self.properties.CLAMP_SIZE, self.properties.CREATE_FGONS, @@ -1621,7 +1604,8 @@ class IMPORT_OT_obj(bpy.types.Operator): return {'RUNNING_MODAL'} -menu_func = lambda self, context: self.layout.operator(IMPORT_OT_obj.bl_idname, text="Wavefront (.obj)") +def menu_func(self, context): + self.layout.operator(IMPORT_OT_obj.bl_idname, text="Wavefront (.obj)") def register(): @@ -1648,4 +1632,3 @@ def unregister(): if __name__ == "__main__": register() - diff --git a/release/scripts/io/import_shape_mdd.py b/release/scripts/io/import_shape_mdd.py index c64fbd15a5c..ec0e7696630 100644 --- a/release/scripts/io/import_shape_mdd.py +++ b/release/scripts/io/import_shape_mdd.py @@ -25,7 +25,7 @@ # origonal model the mdd was Baked out from their will be Strange # behavior # -# vertex animation to ShapeKeys with ipo and gives the frame a value of 1.0 +# vertex animation to ShapeKeys with ipo and gives the frame a value of 1.0 # A modifier to read mdd files would be Ideal but thats for another day :) # # Please send any fixes,updates,bugs to Slow67_at_Gmail.com @@ -36,77 +36,75 @@ from struct import unpack def mdd_import(filepath, ob, scene, PREF_START_FRAME=0, PREF_JUMP=1): - + print('\n\nimporting mdd "%s"' % filepath) - + bpy.ops.object.mode_set(mode='OBJECT') file = open(filepath, 'rb') frames, points = unpack(">2i", file.read(8)) time = unpack((">%df" % frames), file.read(frames * 4)) - - print('\tpoints:%d frames:%d' % (points,frames)) + + print('\tpoints:%d frames:%d' % (points, frames)) # If target object doesn't have Basis shape key, create it. try: - num_keys = len( ob.data.shape_keys.keys ) + num_keys = len(ob.data.shape_keys.keys) except: basis = ob.add_shape_key() basis.name = "Basis" ob.data.update() - scene.current_frame = PREF_START_FRAME + scene.frame_current = PREF_START_FRAME def UpdateMesh(ob, fr): - + # Insert new shape key new_shapekey = ob.add_shape_key() new_shapekey.name = ("frame_%.4d" % fr) new_shapekey_name = new_shapekey.name - + ob.active_shape_key_index = len(ob.data.shape_keys.keys)-1 index = len(ob.data.shape_keys.keys)-1 ob.shape_key_lock = True - - verts = ob.data.shape_keys.keys[ len(ob.data.shape_keys.keys)-1 ].data - - - for v in verts: - # 12 is the size of 3 floats - x,y,z= unpack('>3f', file.read(12)) - v.co[:] = x,z,y + + verts = ob.data.shape_keys.keys[len(ob.data.shape_keys.keys)-1].data + + + for v in verts: # 12 is the size of 3 floats + v.co[:] = unpack('>3f', file.read(12)) #me.update() ob.shape_key_lock = False - + # insert keyframes shape_keys = ob.data.shape_keys - scene.current_frame -= 1 + scene.frame_current -= 1 ob.data.shape_keys.keys[index].value = 0.0 shape_keys.keys[len(ob.data.shape_keys.keys)-1].keyframe_insert("value") - scene.current_frame += 1 + scene.frame_current += 1 ob.data.shape_keys.keys[index].value = 1.0 shape_keys.keys[len(ob.data.shape_keys.keys)-1].keyframe_insert("value") - scene.current_frame += 1 + scene.frame_current += 1 ob.data.shape_keys.keys[index].value = 0.0 shape_keys.keys[len(ob.data.shape_keys.keys)-1].keyframe_insert("value") - ob.data.update() + ob.data.update() for i in range(frames): UpdateMesh(ob, i) - + from bpy.props import * class importMDD(bpy.types.Operator): '''Import MDD vertex keyframe file to shape keys''' - bl_idname = "import.mdd" + bl_idname = "import_shape.mdd" bl_label = "Import MDD" # get first scene to get min and max properties for frames, fps @@ -118,20 +116,19 @@ class importMDD(bpy.types.Operator): # List of operator properties, the attributes will be assigned # to the class instance from the operator settings before calling. - path = StringProperty(name="File Path", description="File path used for importing the MDD file", maxlen=1024) + filepath = StringProperty(name="File Path", description="Filepath used for importing the MDD file", maxlen=1024) #fps = IntProperty(name="Frames Per Second", description="Number of frames/second", min=minfps, max=maxfps, default=25) - start_frame = IntProperty(name="Start Frame", description="Start frame for inserting animation", min=minframe, max=maxframe, default=0) - + frame_start = IntProperty(name="Start Frame", description="Start frame for inserting animation", min=minframe, max=maxframe, default=0) def poll(self, context): ob = context.active_object return (ob and ob.type == 'MESH') def execute(self, context): - if not self.properties.path: + if not self.properties.filepath: raise Exception("filename not set") - mdd_import( self.properties.path, bpy.context.active_object, context.scene, self.properties.start_frame, 1) + mdd_import(self.properties.filepath, bpy.context.active_object, context.scene, self.properties.frame_start, 1) return {'FINISHED'} @@ -148,7 +145,8 @@ def menu_func(self, context): def register(): bpy.types.register(importMDD) bpy.types.INFO_MT_file_import.append(menu_func) - + + def unregister(): bpy.types.unregister(importMDD) bpy.types.INFO_MT_file_import.remove(menu_func) diff --git a/release/scripts/io/netrender/__init__.py b/release/scripts/io/netrender/__init__.py index e06d061f173..f5f104d6d92 100644 --- a/release/scripts/io/netrender/__init__.py +++ b/release/scripts/io/netrender/__init__.py @@ -27,6 +27,7 @@ from netrender import master_html from netrender import utils from netrender import balancing from netrender import ui +from netrender import repath jobs = [] slaves = [] diff --git a/release/scripts/io/netrender/client.py b/release/scripts/io/netrender/client.py index aea9c37ff47..9f6d1a7639e 100644 --- a/release/scripts/io/netrender/client.py +++ b/release/scripts/io/netrender/client.py @@ -96,12 +96,12 @@ def clientSendJob(conn, scene, anim = False): job = netrender.model.RenderJob() if anim: - for f in range(scene.start_frame, scene.end_frame + 1): + for f in range(scene.frame_start, scene.frame_end + 1): job.addFrame(f) else: - job.addFrame(scene.current_frame) + job.addFrame(scene.frame_current) - filename = bpy.data.filename + filename = bpy.data.filepath job.addFile(filename) job_name = netsettings.job_name @@ -113,7 +113,7 @@ def clientSendJob(conn, scene, anim = False): # LIBRARIES ########################### for lib in bpy.data.libraries: - file_path = bpy.utils.expandpath(lib.filename) + file_path = bpy.utils.expandpath(lib.filepath) if os.path.exists(file_path): job.addFile(file_path) @@ -122,9 +122,13 @@ def clientSendJob(conn, scene, anim = False): ########################### for image in bpy.data.images: if image.source == "FILE" and not image.packed_file: - file_path = bpy.utils.expandpath(image.filename) + file_path = bpy.utils.expandpath(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 @@ -144,6 +148,9 @@ def clientSendJob(conn, scene, anim = False): addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path) if modifier.domain_settings.highres: addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path) + elif modifier.type == "MULTIRES" and modifier.external: + file_path = bpy.utils.expandpath(modifier.filepath) + job.addFile(file_path) # particles modifier are stupid and don't contain data # we have to go through the object property @@ -186,6 +193,7 @@ def requestResult(conn, job_id, frame): class NetworkRenderEngine(bpy.types.RenderEngine): bl_idname = 'NET_RENDER' bl_label = "Network Render" + bl_postprocess = False def render(self, scene): if scene.network_render.mode == "RENDER_CLIENT": self.render_client(scene) @@ -227,7 +235,7 @@ class NetworkRenderEngine(bpy.types.RenderEngine): self.update_stats("", "Network render waiting for results") - requestResult(conn, job_id, scene.current_frame) + requestResult(conn, job_id, scene.frame_current) response = conn.getresponse() if response.status == http.client.NO_CONTENT: @@ -235,12 +243,12 @@ class NetworkRenderEngine(bpy.types.RenderEngine): netsettings.job_id = clientSendJob(conn, scene) job_id = netsettings.job_id - requestResult(conn, job_id, scene.current_frame) + requestResult(conn, job_id, scene.frame_current) response = conn.getresponse() while response.status == http.client.ACCEPTED and not self.test_break(): time.sleep(1) - requestResult(conn, job_id, scene.current_frame) + requestResult(conn, job_id, scene.frame_current) response = conn.getresponse() # cancel new jobs (animate on network) on break @@ -268,7 +276,7 @@ class NetworkRenderEngine(bpy.types.RenderEngine): f.close() result = self.begin_result(0, 0, x, y) - result.load_from_file(netsettings.path + "output.exr", 0, 0) + result.load_from_file(netsettings.path + "output.exr") self.end_result(result) conn.close() @@ -283,3 +291,6 @@ def compatible(module): #compatible("properties_render") 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 index 019f33047d8..f227f61a536 100644 --- a/release/scripts/io/netrender/master.py +++ b/release/scripts/io/netrender/master.py @@ -19,6 +19,7 @@ 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 from netrender.utils import * @@ -27,12 +28,16 @@ import netrender.balancing import netrender.master_html class MRenderFile(netrender.model.RenderFile): - def __init__(self, filepath, index, start, end): - super().__init__(filepath, index, start, end) + 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: + found_signature = hashFile(self.filepath) + self.found = self.signature == found_signature + return self.found @@ -74,7 +79,7 @@ class MRenderJob(netrender.model.RenderJob): # special server properties self.last_update = 0 self.save_path = "" - self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end) for rfile in job_info.files] + self.files = [MRenderFile(rfile.filepath, rfile.index, rfile.start, rfile.end, rfile.signature) for rfile in job_info.files] self.resolution = None @@ -190,6 +195,11 @@ 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 send_head(self, code = http.client.OK, headers = {}, content = "application/octet-stream"): self.send_response(code) self.send_header("Content-type", content) @@ -711,7 +721,7 @@ class RenderHandler(http.server.BaseHTTPRequestHandler): buf = self.rfile.read(length) # add same temp file + renames as slave - + f = open(file_path, "wb") f.write(buf) f.close() @@ -861,16 +871,20 @@ class RenderHandler(http.server.BaseHTTPRequestHandler): self.send_head(http.client.NO_CONTENT) class RenderMasterServer(socketserver.ThreadingMixIn, http.server.HTTPServer): - def __init__(self, address, handler_class, path): + 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 - self.path = path + "master_" + str(os.getpid()) + os.sep - self.slave_timeout = 30 # 30 mins: need a parameter for that + if subdir: + self.path = path + "master_" + str(os.getpid()) + os.sep + 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)) @@ -883,6 +897,22 @@ class RenderMasterServer(socketserver.ThreadingMixIn, http.server.HTTPServer): 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) @@ -1001,8 +1031,29 @@ class RenderMasterServer(socketserver.ThreadingMixIn, http.server.HTTPServer): 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 = RenderMasterServer(address, RenderHandler, path) + httpd = createMaster(address, clear, path) httpd.timeout = 1 httpd.stats = update_stats @@ -1010,7 +1061,7 @@ def runMaster(address, broadcast, clear, path, update_stats, test_break): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - start_time = time.time() + start_time = time.time() - 2 while not test_break(): try: @@ -1018,7 +1069,7 @@ def runMaster(address, broadcast, clear, path, update_stats, test_break): except select.error: pass - if time.time() - start_time >= 10: # need constant here + if time.time() - start_time >= 2: # need constant here httpd.timeoutSlaves() httpd.updateUsage() @@ -1031,3 +1082,6 @@ def runMaster(address, broadcast, clear, path, update_stats, test_break): 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 index e5feac86e12..c3695cd4f0f 100644 --- a/release/scripts/io/netrender/master_html.py +++ b/release/scripts/io/netrender/master_html.py @@ -29,6 +29,7 @@ def get(handler): def head(title): output("<html><head>") + 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>") @@ -105,53 +106,6 @@ def get(handler): handler.send_head(content = "text/html") head("NetRender") - output("<h2>Master</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('%i', '%s')" % (id(rule), str(not rule.enabled))), - rule, - rule.str_limit() + - """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else " " - ) - - for rule in handler.server.balancer.priorities: - rowTable( - "priority", - checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))), - rule, - rule.str_limit() + - """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else " " - ) - - for rule in handler.server.balancer.exceptions: - rowTable( - "exception", - checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))), - rule, - rule.str_limit() + - """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else " " - ) - - 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>Jobs</h2>") startTable() @@ -203,6 +157,53 @@ def get(handler): ) 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('%i', '%s')" % (id(rule), str(not rule.enabled))), + rule, + rule.str_limit() + + """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else " " + ) + + for rule in handler.server.balancer.priorities: + rowTable( + "priority", + checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))), + rule, + rule.str_limit() + + """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else " " + ) + + for rule in handler.server.balancer.exceptions: + rowTable( + "exception", + checkbox("", rule.enabled, "balance_enable('%i', '%s')" % (id(rule), str(not rule.enabled))), + rule, + rule.str_limit() + + """<button title="edit limit" onclick="balance_edit('%i', '%s');">edit</button>""" % (id(rule), str(rule.limit)) if hasattr(rule, "limit") else " " + ) + + endTable() output("</body></html>") @@ -234,13 +235,17 @@ def get(handler): tot_cache = 0 tot_fluid = 0 + rowTable(job.files[0].filepath) + rowTable("Other Files", class_style = "toggle", extra = "onclick='toggleDisplay(".other", "none", "table-row")'") + 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: - rowTable(file.filepath) + 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(".cache", "none", "table-row")'") @@ -256,9 +261,9 @@ def get(handler): endTable() - output("<h2>Blacklist</h2>") - if job.blacklist: + output("<h2>Blacklist</h2>") + startTable() headerTable("name", "address") @@ -267,8 +272,6 @@ def get(handler): rowTable(slave.name, slave.address[0]) endTable() - else: - output("<i>Empty</i>") output("<h2>Frames</h2>") diff --git a/release/scripts/io/netrender/model.py b/release/scripts/io/netrender/model.py index 8b0f50ba848..e7656f498b4 100644 --- a/release/scripts/io/netrender/model.py +++ b/release/scripts/io/netrender/model.py @@ -103,8 +103,10 @@ JOB_TYPES = { } class RenderFile: - def __init__(self, filepath = "", index = 0, start = -1, end = -1): + 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 @@ -112,9 +114,11 @@ class RenderFile: def serialize(self): return { "filepath": self.filepath, + "original_path": self.original_path, "index": self.index, "start": self.start, - "end": self.end + "end": self.end, + "signature": self.signature } @staticmethod @@ -122,7 +126,8 @@ class RenderFile: if not data: return None - rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"]) + rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"], data["signature"]) + rfile.original_path = data["original_path"] return rfile @@ -153,7 +158,8 @@ class RenderJob: self.blacklist = job_info.blacklist def addFile(self, file_path, start=-1, end=-1): - self.files.append(RenderFile(file_path, len(self.files), start, end)) + signature = hashFile(file_path) + self.files.append(RenderFile(file_path, len(self.files), start, end, signature)) def addFrame(self, frame_number, command = ""): frame = RenderFrame(frame_number, command) diff --git a/release/scripts/io/netrender/netrender.css b/release/scripts/io/netrender/netrender.css index cc8a93bb9a7..0c54690e002 100644 --- a/release/scripts/io/netrender/netrender.css +++ b/release/scripts/io/netrender/netrender.css @@ -68,6 +68,10 @@ button { display: none; } +.other { + display: none; +} + .rules { width: 60em; text-align: left; diff --git a/release/scripts/io/netrender/operators.py b/release/scripts/io/netrender/operators.py index f667be68c84..858ec800dbc 100644 --- a/release/scripts/io/netrender/operators.py +++ b/release/scripts/io/netrender/operators.py @@ -39,7 +39,7 @@ class RENDER_OT_netslave_bake(bpy.types.Operator): scene = context.scene netsettings = scene.network_render - filename = bpy.data.filename + 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 @@ -77,7 +77,7 @@ class RENDER_OT_netslave_bake(bpy.types.Operator): bpy.ops.ptcache.bake_all() - #bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend") + #bpy.ops.wm.save_mainfile(filepath = path + os.sep + root + "_baked.blend") return {'FINISHED'} @@ -104,7 +104,7 @@ class RENDER_OT_netclientanim(bpy.types.Operator): scene.network_render.job_id = client.clientSendJob(conn, scene, True) conn.close() - bpy.ops.screen.render('INVOKE_AREA', animation=True) + bpy.ops.render.render('INVOKE_AREA', animation=True) return {'FINISHED'} @@ -121,7 +121,7 @@ class RENDER_OT_netclientrun(bpy.types.Operator): return True def execute(self, context): - bpy.ops.screen.render('INVOKE_AREA', animation=True) + bpy.ops.render.render('INVOKE_AREA', animation=True) return {'FINISHED'} @@ -159,6 +159,36 @@ class RENDER_OT_netclientsend(bpy.types.Operator): return self.execute(context) @rnaType +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" + + def poll(self, 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) + +@rnaType class RENDER_OT_netclientstatus(bpy.types.Operator): '''Refresh the status of the current jobs''' bl_idname = "render.netclientstatus" diff --git a/release/scripts/io/netrender/repath.py b/release/scripts/io/netrender/repath.py new file mode 100755 index 00000000000..7f9befd34fb --- /dev/null +++ b/release/scripts/io/netrender/repath.py @@ -0,0 +1,147 @@ +# ##### 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.external = False + + def processFluid(fluid): + new_path = path_map.get(fluid.path, 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[paths[i]] = paths[i+1] + + ########################### + # LIBRARIES + ########################### + for lib in bpy.data.libraries: + file_path = bpy.utils.expandpath(lib.filepath) + new_path = path_map.get(file_path, 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.utils.expandpath(image.filepath) + new_path = path_map.get(file_path, 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.highres: + processPointCache(modifier.domain_settings.point_cache_high) + elif modifier.type == "MULTIRES" and modifier.external: + file_path = bpy.utils.expandpath(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(path=new_path, check_existing=False) diff --git a/release/scripts/io/netrender/slave.py b/release/scripts/io/netrender/slave.py index d24490385fb..9fd00152dc1 100644 --- a/release/scripts/io/netrender/slave.py +++ b/release/scripts/io/netrender/slave.py @@ -22,6 +22,7 @@ import subprocess, time from netrender.utils import * import netrender.model +import netrender.repath BLENDER_PATH = sys.argv[0] @@ -64,12 +65,22 @@ def testCancel(conn, job_id, frame_number): else: return False -def testFile(conn, job_id, slave_id, file_index, JOB_PREFIX, file_path, main_path = None): - job_full_path = prefixPath(JOB_PREFIX, file_path, main_path) - - if not os.path.exists(job_full_path): - temp_path = JOB_PREFIX + "slave.temp.blend" - conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id}) +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: + 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: + temp_path = 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: @@ -85,6 +96,8 @@ def testFile(conn, job_id, slave_id, file_index, JOB_PREFIX, file_path, main_pat f.close() os.renames(temp_path, job_full_path) + + rfile.filepath = job_full_path return job_full_path @@ -105,6 +118,8 @@ def render_slave(engine, netsettings, threads): 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() @@ -113,6 +128,7 @@ def render_slave(engine, netsettings, threads): timeout = 1 # reset timeout on new job job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8'))) + engine.update_stats("", "Network render processing job from master") JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep if not os.path.exists(JOB_PREFIX): @@ -123,14 +139,17 @@ def render_slave(engine, netsettings, threads): 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, 0, JOB_PREFIX, 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,)) - engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id) for rfile in job.files[1:]: + testFile(conn, job.id, slave_id, rfile, JOB_PREFIX, main_path) print("\t", rfile.filepath) - testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path) + + netrender.repath.update(job) + + 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]) @@ -175,6 +194,10 @@ def render_slave(engine, netsettings, threads): # (only need to update on one frame, they are linked conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers) response = conn.getresponse() + + # Also output on console + if netsettings.slave_thumb: + print(str(stdout, encoding='utf8'), end="") stdout = bytes() @@ -182,6 +205,9 @@ def render_slave(engine, netsettings, threads): 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() @@ -191,6 +217,17 @@ def render_slave(engine, netsettings, threads): process.terminate() continue # to next frame + # flush the rest of the logs + if stdout: + # Also output on console + if netsettings.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 conn.getresponse().status == http.client.NO_CONTENT: + continue + total_t = time.time() - start_t avg_t = total_t / len(job.frames) @@ -199,13 +236,6 @@ def render_slave(engine, netsettings, threads): print("status", status) - # flush the rest of the logs - if stdout: - # (only need to update on one frame, they are linked - conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers) - if conn.getresponse().status == http.client.NO_CONTENT: - continue - headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)} @@ -245,6 +275,8 @@ def render_slave(engine, netsettings, threads): conn.request("PUT", "/render", headers=headers) if conn.getresponse().status == http.client.NO_CONTENT: continue + + engine.update_stats("", "Network render connected to master, waiting for jobs") else: if timeout < MAX_TIMEOUT: timeout += INCREMENT_TIMEOUT diff --git a/release/scripts/io/netrender/ui.py b/release/scripts/io/netrender/ui.py index ba14d7e4f42..c60b10c484a 100644 --- a/release/scripts/io/netrender/ui.py +++ b/release/scripts/io/netrender/ui.py @@ -37,8 +37,8 @@ DONE = 2 ERROR = 3 def init_file(): - if netrender.init_file != bpy.data.filename: - netrender.init_file = bpy.data.filename + if netrender.init_file != bpy.data.filepath: + netrender.init_file = bpy.data.filepath netrender.init_data = True netrender.init_address = True @@ -76,7 +76,7 @@ def verify_address(netsettings): else: netsettings.server_address = "[default]" -class RenderButtonsPanel(bpy.types.Panel): +class RenderButtonsPanel(): bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "render" @@ -88,7 +88,7 @@ class RenderButtonsPanel(bpy.types.Panel): # Setting panel, use in the scene for now. @rnaType -class RENDER_PT_network_settings(RenderButtonsPanel): +class RENDER_PT_network_settings(bpy.types.Panel, RenderButtonsPanel): bl_label = "Network Settings" COMPAT_ENGINES = {'NET_RENDER'} @@ -123,7 +123,7 @@ class RENDER_PT_network_settings(RenderButtonsPanel): layout.operator("render.netclientweb", icon='QUESTION') @rnaType -class RENDER_PT_network_slave_settings(RenderButtonsPanel): +class RENDER_PT_network_slave_settings(bpy.types.Panel, RenderButtonsPanel): bl_label = "Slave Settings" COMPAT_ENGINES = {'NET_RENDER'} @@ -141,13 +141,14 @@ class RENDER_PT_network_slave_settings(RenderButtonsPanel): layout.prop(netsettings, "slave_clear") layout.prop(netsettings, "slave_thumb") + layout.prop(netsettings, "slave_outputlog") layout.label(text="Threads:") layout.prop(rd, "threads_mode", expand=True) sub = layout.column() - sub.enabled = rd.threads_mode == 'THREADS_FIXED' + sub.enabled = rd.threads_mode == 'FIXED' sub.prop(rd, "threads") @rnaType -class RENDER_PT_network_master_settings(RenderButtonsPanel): +class RENDER_PT_network_master_settings(bpy.types.Panel, RenderButtonsPanel): bl_label = "Master Settings" COMPAT_ENGINES = {'NET_RENDER'} @@ -166,7 +167,7 @@ class RENDER_PT_network_master_settings(RenderButtonsPanel): layout.prop(netsettings, "master_clear") @rnaType -class RENDER_PT_network_job(RenderButtonsPanel): +class RENDER_PT_network_job(bpy.types.Panel, RenderButtonsPanel): bl_label = "Job Settings" COMPAT_ENGINES = {'NET_RENDER'} @@ -186,10 +187,11 @@ class RENDER_PT_network_job(RenderButtonsPanel): 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("screen.render", text="Get Image", icon='RENDER_STILL') - row.operator("screen.render", text="Get Animation", icon='RENDER_ANIMATION').animation = True + 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) @@ -206,16 +208,17 @@ class RENDER_PT_network_job(RenderButtonsPanel): row.prop(netsettings, "chunks") @rnaType -class RENDER_PT_network_slaves(RenderButtonsPanel): +class RENDER_PT_network_slaves(bpy.types.Panel, RenderButtonsPanel): bl_label = "Slaves Status" COMPAT_ENGINES = {'NET_RENDER'} def poll(self, context): scene = context.scene netsettings = scene.network_render + if netsettings.mode != "RENDER_CLIENT": + return False verify_address(netsettings) return (super().poll(context) - and netsettings.mode == "RENDER_CLIENT" and netsettings.server_address != "[default]") def draw(self, context): @@ -244,16 +247,17 @@ class RENDER_PT_network_slaves(RenderButtonsPanel): layout.label(text="Stats: " + slave.stats) @rnaType -class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel): +class RENDER_PT_network_slaves_blacklist(bpy.types.Panel, RenderButtonsPanel): bl_label = "Slaves Blacklist" COMPAT_ENGINES = {'NET_RENDER'} def poll(self, context): scene = context.scene netsettings = scene.network_render + if netsettings.mode != "RENDER_CLIENT": + return False verify_address(netsettings) return (super().poll(context) - and netsettings.mode == "RENDER_CLIENT" and netsettings.server_address != "[default]") def draw(self, context): @@ -281,16 +285,17 @@ class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel): layout.label(text="Stats: " + slave.stats) @rnaType -class RENDER_PT_network_jobs(RenderButtonsPanel): +class RENDER_PT_network_jobs(bpy.types.Panel, RenderButtonsPanel): bl_label = "Jobs" COMPAT_ENGINES = {'NET_RENDER'} def poll(self, context): scene = context.scene netsettings = scene.network_render + if netsettings.mode != "RENDER_CLIENT": + return False verify_address(netsettings) return (super().poll(context) - and netsettings.mode == "RENDER_CLIENT" and netsettings.server_address != "[default]") def draw(self, context): @@ -362,6 +367,11 @@ NetRenderSettings.BoolProperty( attr="slave_thumb", description="Generate thumbnails on slaves instead of master", default = False) +NetRenderSettings.BoolProperty( attr="slave_outputlog", + name="Output render log on console", + description="Output render text log to console as well as sending it to the master", + default = True) + NetRenderSettings.BoolProperty( attr="master_clear", name="Clear on exit", description="delete saved files on exit", diff --git a/release/scripts/io/netrender/utils.py b/release/scripts/io/netrender/utils.py index 1d35ea00c99..6288b9747c0 100644 --- a/release/scripts/io/netrender/utils.py +++ b/release/scripts/io/netrender/utils.py @@ -19,7 +19,7 @@ import sys, os import re import http, http.client, http.server, urllib, socket -import subprocess, shutil, time, hashlib +import subprocess, shutil, time, hashlib, zlib import netrender.model @@ -28,7 +28,7 @@ try: except: bpy = None -VERSION = bytes("0.8", encoding='utf8') +VERSION = bytes("0.9", encoding='utf8') # Jobs status JOB_WAITING = 0 # before all data has been entered @@ -110,7 +110,7 @@ def clientConnection(address, port, report = None, scan = True): return None try: - conn = http.client.HTTPConnection(address, port) + conn = http.client.HTTPConnection(address, port, timeout = 5) if conn: if clientVerifyVersion(conn): @@ -154,18 +154,33 @@ def renderURL(job_id, frame_number): def cancelURL(job_id): return "/cancel_%s" % (job_id) -def prefixPath(prefix_directory, file_path, prefix_path): +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): # if an absolute path, make sure path exists, if it doesn't, use relative local path full_path = file_path - if not os.path.exists(full_path): + if force or not os.path.exists(full_path): p, n = os.path.split(full_path) if prefix_path and p.startswith(prefix_path): - directory = prefix_directory + p[len(prefix_path):] - full_path = directory + os.sep + n - if not os.path.exists(directory): - os.mkdir(directory) + if len(prefix_path) < len(p): + directory = prefix_directory + p[len(prefix_path)+1:] + os.sep # +1 to remove separator + if not os.path.exists(directory): + os.mkdir(directory) + else: + directory = prefix_directory + full_path = directory + n else: full_path = prefix_directory + n else: @@ -199,8 +214,8 @@ def thumbnail(filename): if bpy: scene = bpy.data.scenes[0] # FIXME, this is dodgy! scene.render.file_format = "JPEG" - scene.render.quality = 90 - bpy.ops.image.open(path = filename) + scene.render.file_quality = 90 + bpy.ops.image.open(filepath=filename) img = bpy.data.images[imagename] img.save_render(thumbname, scene=scene) diff --git a/release/scripts/keyingsets/keyingsets_builtins.py b/release/scripts/keyingsets/keyingsets_builtins.py new file mode 100644 index 00000000000..bf5c66ad01d --- /dev/null +++ b/release/scripts/keyingsets/keyingsets_builtins.py @@ -0,0 +1,225 @@ +# 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. + +import bpy +from keyingsets_utils import * + +############################### +# Built-In KeyingSets + +# Location +class BUILTIN_KSI_Location(bpy.types.KeyingSetInfo): + bl_label = "Location" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + generate = RKS_GEN_location + +# Rotation +class BUILTIN_KSI_Rotation(bpy.types.KeyingSetInfo): + bl_label = "Rotation" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + generate = RKS_GEN_rotation + +# Scale +class BUILTIN_KSI_Scaling(bpy.types.KeyingSetInfo): + bl_label = "Scaling" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + generate = RKS_GEN_scaling + +# ------------ + +# LocRot +class BUILTIN_KSI_LocRot(bpy.types.KeyingSetInfo): + bl_label = "LocRot" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + def generate(self, context, ks, data): + # location + RKS_GEN_location(self, context, ks, data) + # rotation + RKS_GEN_rotation(self, context, ks, data) + +# LocScale +class BUILTIN_KSI_LocScale(bpy.types.KeyingSetInfo): + bl_label = "LocScale" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + def generate(self, context, ks, data): + # location + RKS_GEN_location(self, context, ks, data) + # scale + RKS_GEN_scaling(self, context, ks, data) + +# LocRotScale +class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo): + bl_label = "LocRotScale" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + def generate(self, context, ks, data): + # location + RKS_GEN_location(self, context, ks, data) + # rotation + RKS_GEN_rotation(self, context, ks, data) + # scale + RKS_GEN_scaling(self, context, ks, data) + +# RotScale +class BUILTIN_KSI_RotScale(bpy.types.KeyingSetInfo): + bl_label = "RotScale" + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + def generate(self, context, ks, data): + # rotation + RKS_GEN_rotation(self, context, ks, data) + # scaling + RKS_GEN_scaling(self, context, ks, data) + +# ------------ + +# Location +class BUILTIN_KSI_VisualLoc(bpy.types.KeyingSetInfo): + bl_label = "Visual Location" + + insertkey_visual = True + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + generate = RKS_GEN_location + +# Rotation +class BUILTIN_KSI_VisualRot(bpy.types.KeyingSetInfo): + bl_label = "Visual Rotation" + + insertkey_visual = True + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + generate = RKS_GEN_rotation + +# VisualLocRot +class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo): + bl_label = "Visual LocRot" + + insertkey_visual = True + + # poll - use predefined callback for selected bones/objects + poll = RKS_POLL_selected_items + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + def generate(self, context, ks, data): + # location + RKS_GEN_location(self, context, ks, data) + # rotation + RKS_GEN_rotation(self, context, ks, data) + +# ------------ + +# Available +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 + + # iterator - use callback for selected bones/objects + iterator = RKS_ITER_selected_item + + # generator - use callback for location + generate = RKS_GEN_available + +############################### + +classes = [ + BUILTIN_KSI_Location, + BUILTIN_KSI_Rotation, + BUILTIN_KSI_Scaling, + + BUILTIN_KSI_LocRot, + BUILTIN_KSI_LocScale, + BUILTIN_KSI_LocRotScale, + BUILTIN_KSI_RotScale, + + BUILTIN_KSI_VisualLoc, + BUILTIN_KSI_VisualRot, + BUILTIN_KSI_VisualLocRot, + + BUILTIN_KSI_Available, +] + + +def register(): + register = bpy.types.register + for cls in classes: + register(cls) + + +def unregister(): + unregister = bpy.types.unregister + for cls in classes: + unregister(cls) + +if __name__ == "__main__": + register() + +############################### diff --git a/release/scripts/keyingsets/keyingsets_utils.py b/release/scripts/keyingsets/keyingsets_utils.py new file mode 100644 index 00000000000..77b0f3ebacd --- /dev/null +++ b/release/scripts/keyingsets/keyingsets_utils.py @@ -0,0 +1,163 @@ +# This file defines a set of methods that are useful for various +# Relative Keying Set (RKS) related operations, such as: callbacks +# for polling, iterator callbacks, and also generate callbacks. +# All of these can be used in conjunction with the others. + +import bpy + +########################### +# General Utilities + +# Append the specified property name on the the existing path +def path_add_property(path, prop): + if len(path): + return path + "." + prop; + else: + return prop; + +########################### +# Poll Callbacks + +# selected objects +def RKS_POLL_selected_objects(ksi, context): + return context.active_object or len(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): + return True; + + # nothing selected + return False; + + +# selected bones or objects +def RKS_POLL_selected_items(ksi, context): + return RKS_POLL_selected_bones(ksi, context) or RKS_POLL_selected_objects(ksi, context); + +########################### +# Iterator Callbacks + +# 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'): + for bone in context.selected_pose_bones: + ksi.generate(context, ks, bone) + else: + for ob in context.selected_objects: + ksi.generate(context, ks, ob) + +########################### +# Generate Callbacks + +# 'Available' F-Curves +def RKS_GEN_available(ksi, context, ks, data): + # try to get the animation data associated with the closest + # ID-block to the data (neither of which may exist/be easy to find) + id_block = data.id_data + adt = getattr(id_block, "animation_data", None) + + # there must also be an active action... + if adt is None or adt.action is None: + return; + + # for each F-Curve, include an path to key it + # NOTE: we don't need to set the group settings here + for fcu in adt.action.fcurves: + ks.paths.add(id_block, fcu.data_path, index=fcu.array_index) + +# ------ + +# get ID block and based ID path for transform generators +def get_transform_generators_base_info(data): + # ID-block for the data + id_block = data.id_data + + # get base path and grouping method/name + if isinstance(data, bpy.types.ID): + # no path in this case + path = "" + + # data on ID-blocks directly should get grouped by the KeyingSet + grouping = None + else: + # get the path to the ID-block + path = data.path_from_id() + + # try to use the name of the data element to group the F-Curve + # else fallback on the KeyingSet name + grouping = getattr(data, "name", None) + + # return the ID-block and the path + return id_block, path, grouping + +# Location +def RKS_GEN_location(ksi, context, ks, data): + # get id-block and path info + id_block, base_path, grouping= get_transform_generators_base_info(data) + + # add the property name to the base path + path = path_add_property(base_path, "location") + + # add Keying Set entry for this... + if grouping: + ks.paths.add(id_block, path, grouping_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path) + +# Rotation +def RKS_GEN_rotation(ksi, context, ks, data): + # get id-block and path info + id_block, base_path, grouping= 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, "rotation_quaternion") + elif data.rotation_mode == 'AXISANGLE': + path = path_add_property(base_path, "rotation_axis_angle") + else: + path = path_add_property(base_path, "rotation_euler") + + # add Keying Set entry for this... + if grouping: + ks.paths.add(id_block, path, grouping_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path) + +# Scaling +def RKS_GEN_scaling(ksi, context, ks, data): + # get id-block and path info + id_block, base_path, grouping= get_transform_generators_base_info(data) + + # add the property name to the base path + path = path_add_property(base_path, "scale") + + # add Keying Set entry for this... + if grouping: + ks.paths.add(id_block, path, grouping_method='NAMED', group_name=grouping) + else: + ks.paths.add(id_block, path) + +########################### +# Un-needed stuff which is here to just shut up the warnings... + +classes = [] + +def register(): + register = bpy.types.register + for cls in classes: + register(cls) + + +def unregister(): + unregister = bpy.types.unregister + for cls in classes: + unregister(cls) + +if __name__ == "__main__": + register() + +########################### diff --git a/release/scripts/modules/add_object_utils.py b/release/scripts/modules/add_object_utils.py new file mode 100644 index 00000000000..cef368c529d --- /dev/null +++ b/release/scripts/modules/add_object_utils.py @@ -0,0 +1,83 @@ +# ##### 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 + + +def add_object_align_init(context, operator): + + if operator and operator.properties.is_property_set("location") and operator.properties.is_property_set("rotation"): + location = mathutils.TranslationMatrix(mathutils.Vector(operator.properties.location)) + rotation = mathutils.Euler(operator.properties.rotation).to_matrix().resize4x4() + else: + # TODO, local view cursor! + location = mathutils.TranslationMatrix(context.scene.cursor_location) + + if context.user_preferences.edit.object_align == 'VIEW' and context.space_data.type == 'VIEW_3D': + rotation = context.space_data.region_3d.view_matrix.rotation_part().invert().resize4x4() + else: + rotation = mathutils.Matrix() + + # set the operator properties + if operator: + operator.properties.location = location.translation_part() + operator.properties.rotation = rotation.to_euler() + + return location * rotation + + +def add_object_data(context, obdata, operator=None): + + scene = context.scene + + # ugh, could be made nicer + for ob in scene.objects: + ob.selected = False + + obj_new = bpy.data.objects.new(obdata.name, obdata) + + base = scene.objects.link(obj_new) + base.selected = True + + if context.space_data and context.space_data.type == 'VIEW_3D': + base.layers_from_view(context.space_data) + + + obj_new.matrix_world = add_object_align_init(context, operator) + + obj_act = scene.objects.active + + if obj_act and obj_act.mode == 'EDIT' and obj_act.type == obj_new.type: + bpy.ops.object.mode_set(mode='OBJECT') + + obj_act.selected = True + scene.update() # apply location + #scene.objects.active = obj_new + + bpy.ops.object.join() # join into the active. + + bpy.ops.object.mode_set(mode='EDIT') + else: + scene.objects.active = obj_new + if context.user_preferences.edit.enter_edit_mode: + bpy.ops.object.mode_set(mode='EDIT') + + return base diff --git a/release/scripts/modules/blend_render_info.py b/release/scripts/modules/blend_render_info.py new file mode 100755 index 00000000000..dcd1f9cbee3 --- /dev/null +++ b/release/scripts/modules/blend_render_info.py @@ -0,0 +1,100 @@ +#!/usr/bin/python + +# ##### 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> + +# This module can get render info without running from inside blender. +# +# This struct wont change according to Ton. +# Note that the size differs on 32/64bit +# +# typedef struct BHead { +# int code, len; +# void *old; +# int SDNAnr, nr; +# } BHead; + + +def read_blend_rend_chunk(path): + + import struct + + blendfile = open(path, 'rb') + + head = blendfile.read(7) + + if head[0:2] == b'\x1f\x8b': # gzip magic + import gzip + blendfile.close() + blendfile = gzip.open(path, 'rb') + head = blendfile.read(7) + + if head != b'BLENDER': + print("not a blend file:", path) + blendfile.close() + return [] + + is_64_bit = (blendfile.read(1) == b'-') + + # true for PPC, false for X86 + is_big_endian = (blendfile.read(1) == b'V') + + # Now read the bhead chunk!!! + blendfile.read(3) # skip the version + + scenes = [] + + sizeof_bhead = 24 if is_64_bit else 20 + + while blendfile.read(4) == b'REND': + sizeof_bhead_left = sizeof_bhead - 4 + + struct.unpack('>i' if is_big_endian else '<i', blendfile.read(4))[0] + sizeof_bhead_left -= 4 + + # We dont care about the rest of the bhead struct + blendfile.read(sizeof_bhead_left) + + # Now we want the scene name, start and end frame. this is 32bites long + start_frame, end_frame = struct.unpack('>2i' if is_big_endian else '<2i', blendfile.read(8)) + + scene_name = blendfile.read(24) + + scene_name = scene_name[:scene_name.index(b'\0')] + + try: + scene_name = str(scene_name, 'utf8') + except TypeError: + pass + + scenes.append((start_frame, end_frame, scene_name)) + + return scenes + + +def main(): + import sys + for arg in sys.argv[1:]: + if arg.lower().endswith('.blend'): + for value in read_blend_rend_chunk(arg): + print("%d %d %s" % value) + +if __name__ == '__main__': + main() diff --git a/release/scripts/modules/bpy/__init__.py b/release/scripts/modules/bpy/__init__.py index 12f8e19cef4..1e6db441599 100644 --- a/release/scripts/modules/bpy/__init__.py +++ b/release/scripts/modules/bpy/__init__.py @@ -43,10 +43,13 @@ def _main(): ## people need to explain how this is even a fix. # _sys.path[:] = filter(None, _sys.path) - # a bit nasty but this prevents help() and input() from locking blender - # Ideally we could have some way for the console to replace sys.stdin but - # python would lock blender while waiting for a return value, not easy :| - _sys.stdin = None + # 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.bold = lambda self, text: text + # if "-d" in sys.argv: # Enable this to measure startup speed if 0: diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py index 7787d9ee5fe..5cde7091257 100644 --- a/release/scripts/modules/bpy/utils.py +++ b/release/scripts/modules/bpy/utils.py @@ -27,7 +27,7 @@ import bpy as _bpy import os as _os import sys as _sys -from _bpy import home_paths +from _bpy import home_paths, blend_paths def _test_import(module_name, loaded_modules): @@ -144,6 +144,9 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): _loaded.append(mod) if reload_scripts: + + # TODO, this is broken but should work, needs looking into + ''' # reload modules that may not be directly included for type_class_name in dir(_bpy.types): type_class = getattr(_bpy.types, type_class_name) @@ -156,6 +159,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): for module_name in sorted(loaded_modules): print("Reloading:", module_name) test_reload(_sys.modules[module_name]) + ''' # loop over and unload all scripts _loaded.reverse() @@ -166,23 +170,26 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): unregister() except: traceback.print_exc() + + for mod in _loaded: + reload(mod) + _loaded[:] = [] - for base_path in script_paths(user=False): - for path_subdir in ("ui", "op", "io", "cfg"): + user_path = user_script_path() + + for base_path in script_paths(): + for path_subdir in ("", "ui", "op", "io", "cfg", "keyingsets", "modules"): path = _os.path.join(base_path, path_subdir) if _os.path.isdir(path): sys_path_ensure(path) - for mod in modules_from_path(path, loaded_modules): - test_register(mod) + # only add this to sys.modules, dont run + if path_subdir == "modules": + continue - user_path = user_script_path() - if user_path: - for path_subdir in ("", "ui", "op", "io", "cfg"): - path = _os.path.join(user_path, path_subdir) - if _os.path.isdir(path): - sys_path_ensure(path) + 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) @@ -206,8 +213,26 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): def expandpath(path): + """ + Returns the absolute path relative to the current blend file using the "//" prefix. + """ if path.startswith("//"): - return _os.path.join(_os.path.dirname(_bpy.data.filename), path[2:]) + return _os.path.join(_os.path.dirname(_bpy.data.filepath), path[2:]) + + return path + + +def relpath(path, start=None): + """ + Returns the path relative to the current blend file using the "//" prefix. + + :arg start: Relative to this path, when not set the current filename is used. + :type start: string + """ + if not path.startswith("//"): + if start is None: + start = _os.path.dirname(_bpy.data.filepath) + return "//" + _os.path.relpath(path, start) return path @@ -315,3 +340,51 @@ def preset_paths(subdir): ''' return (_os.path.join(_presets, subdir), ) + + +def smpte_from_seconds(time, fps=None): + ''' + Returns an SMPTE formatted string from the time in seconds: "HH:MM:SS:FF". + + If the fps is not given the current scene is used. + ''' + import math + + if fps is None: + fps = _bpy.context.scene.render.fps + + hours = minutes = seconds = frames = 0 + + if time < 0: + time = - time + neg = "-" + else: + neg = "" + + if time >= 3600.0: # hours + hours = int(time / 3600.0) + time = time % 3600.0 + if time >= 60.0: # mins + minutes = int(time / 60.0) + time = time % 60.0 + + seconds = int(time) + frames = int(round(math.floor(((time - seconds) * fps)))) + + return "%s%02d:%02d:%02d:%02d" % (neg, hours, minutes, seconds, frames) + + +def smpte_from_frame(frame, fps=None, fps_base=None): + ''' + Returns an SMPTE formatted string from the frame: "HH:MM:SS:FF". + + If the fps and fps_base are not given the current scene is used. + ''' + + if fps is None: + fps = _bpy.context.scene.render.fps + + if fps_base is None: + fps_base = _bpy.context.scene.render.fps_base + + return smpte_from_seconds((frame * fps_base) / fps, fps) diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 060de711637..9d6f5ce7b2f 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -19,7 +19,7 @@ # <pep8 compliant> from _bpy import types as bpy_types -from Mathutils import Vector +from mathutils import Vector StructRNA = bpy_types.Struct.__bases__[0] # StructRNA = bpy_types.Struct @@ -29,15 +29,62 @@ class Context(StructRNA): __slots__ = () def copy(self): + from types import BuiltinMethodType new_context = {} - generic_keys = StructRNA.__dict__.keys() - for item in dir(self): - if item not in generic_keys: - new_context[item] = getattr(self, item) + generic_attrs = list(StructRNA.__dict__.keys()) + ["bl_rna", "rna_type", "copy"] + for attr in dir(self): + if not (attr.startswith("_") or attr in generic_attrs): + value = getattr(self, attr) + if type(value) != BuiltinMethodType: + new_context[attr] = value return new_context +class Library(bpy_types.ID): + __slots__ = () + + @property + def users_id(self): + """ID datablocks which use this library""" + import bpy + + # See: readblenentry.c, IDTYPE_FLAGS_ISLINKABLE, we could make this an attribute in rna. + attr_links = "actions", "armatures", "brushes", "cameras", \ + "curves", "gpencil", "groups", "images", \ + "lamps", "lattices", "materials", "metaballs", \ + "meshes", "node_groups", "objects", "scenes", \ + "sounds", "textures", "texts", "fonts", "worlds" + + return tuple(id_block for attr in attr_links for id_block in getattr(bpy.data, attr) if id_block.library == self) + + +class Texture(bpy_types.ID): + __slots__ = () + + @property + def users_material(self): + """Materials that use this texture""" + import bpy + return tuple(mat for mat in bpy.data.materials if self in [slot.texture for slot in mat.texture_slots if slot]) + + @property + def users_object_modifier(self): + """Object modifiers that use this texture""" + import bpy + return tuple(obj for obj in bpy.data.objects if self in [mod.texture for mod in obj.modifiers if mod.type == 'DISPLACE']) + + +class Group(bpy_types.ID): + __slots__ = () + + @property + def users_dupli_group(self): + """The dupli group this group is used in""" + import bpy + return tuple(obj for obj in bpy.data.objects if self == obj.dupli_group) + + class Object(bpy_types.ID): __slots__ = () @@ -45,21 +92,19 @@ class Object(bpy_types.ID): def children(self): """All the children of this object""" import bpy - return [child for child in bpy.data.objects if child.parent == self] + return tuple(child for child in bpy.data.objects if child.parent == self) @property - def group_users(self): + def users_group(self): """The groups this object is in""" import bpy - name = self.name - return [group for group in bpy.data.groups if name in group.objects] + return tuple(group for group in bpy.data.groups if self in group.objects[:]) @property - def scene_users(self): + def users_scene(self): """The scenes this object is in""" import bpy - name = self.name - return [scene for scene in bpy.data.scenes if name in scene.objects] + return tuple(scene for scene in bpy.data.scenes if self in scene.objects[:]) class _GenericBone: @@ -95,19 +140,19 @@ class _GenericBone: def x_axis(self): """ Vector pointing down the x-axis of the bone. """ - return self.matrix.rotation_part() * Vector(1.0, 0.0, 0.0) + return self.matrix.rotation_part() * Vector((1.0, 0.0, 0.0)) @property def y_axis(self): """ Vector pointing down the x-axis of the bone. """ - return self.matrix.rotation_part() * Vector(0.0, 1.0, 0.0) + return self.matrix.rotation_part() * Vector((0.0, 1.0, 0.0)) @property def z_axis(self): """ Vector pointing down the x-axis of the bone. """ - return self.matrix.rotation_part() * Vector(0.0, 0.0, 1.0) + return self.matrix.rotation_part() * Vector((0.0, 0.0, 1.0)) @property def basename(self): @@ -236,8 +281,8 @@ class EditBone(StructRNA, _GenericBone): Transform the the bones head, tail, roll and envalope (when the matrix has a scale component). Expects a 4x4 or 3x3 matrix. """ - from Mathutils import Vector - z_vec = self.matrix.rotation_part() * Vector(0.0, 0.0, 1.0) + from mathutils import Vector + z_vec = self.matrix.rotation_part() * Vector((0.0, 0.0, 1.0)) self.tail = matrix * self.tail self.head = matrix * self.head scalar = matrix.median_scale @@ -303,7 +348,7 @@ class Mesh(bpy_types.ID): edge_face_count_dict = self.edge_face_count_dict return [edge_face_count_dict.get(ed.key, 0) for ed in self.edges] - def edge_loops(self, faces=None, seams=()): + def edge_loops_from_faces(self, faces=None, seams=()): """ Edge loops defined by faces @@ -314,7 +359,7 @@ class Mesh(bpy_types.ID): return a list of edge key lists [ [(0,1), (4, 8), (3,8)], ...] - optionaly, seams are edge keys that will be removed + return a list of edge vertex index lists """ OTHER_INDEX = 2, 3, 0, 1 # opposite face index @@ -379,6 +424,70 @@ class Mesh(bpy_types.ID): return edge_loops + def edge_loops_from_edges(self, edges=None): + """ + Edge loops defined by edges + + Takes me.edges or a list of edges and returns the edge loops + + return a list of vertex indices. + [ [1, 6, 7, 2], ...] + + closed loops have matching start and end values. + """ + line_polys = [] + + # Get edges not used by a face + if edges is None: + edges = self.edges + + if not hasattr(edges, "pop"): + edges = edges[:] + + edge_dict = {ed.key: ed for ed in self.edges if ed.selected} + + while edges: + current_edge = edges.pop() + vert_end, vert_start = current_edge.verts[:] + line_poly = [vert_start, vert_end] + + ok = True + while ok: + ok = False + #for i, ed in enumerate(edges): + i = len(edges) + while i: + i -= 1 + ed = edges[i] + v1, v2 = ed.verts + if v1 == vert_end: + line_poly.append(v2) + vert_end = line_poly[-1] + ok = 1 + del edges[i] + # break + elif v2 == vert_end: + line_poly.append(v1) + vert_end = line_poly[-1] + ok = 1 + del edges[i] + #break + elif v1 == vert_start: + line_poly.insert(0, v2) + vert_start = line_poly[0] + ok = 1 + del edges[i] + # break + elif v2 == vert_start: + line_poly.insert(0, v1) + vert_start = line_poly[0] + ok = 1 + del edges[i] + #break + line_polys.append(line_poly) + + return line_polys + class MeshEdge(StructRNA): __slots__ = () @@ -410,6 +519,24 @@ class MeshFace(StructRNA): return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[3]), ord_ind(verts[3], verts[0]) +class Text(bpy_types.ID): + __slots__ = () + + def as_string(self): + """Return the text as a string.""" + return "\n".join(line.line for line in self.lines) + + def from_string(self, string): + """Replace text with this string.""" + self.clear() + self.write(string) + + @property + def users_logic(self): + """Logic bricks that use this text""" + import bpy + return tuple(obj for obj in bpy.data.objects if self in [cont.text for cont in obj.game.controllers if cont.type == 'PYTHON']) + import collections @@ -490,9 +617,9 @@ class Header(StructRNA, _GenericUI): class Menu(StructRNA, _GenericUI): __slots__ = () - def path_menu(self, searchpaths, operator): + def path_menu(self, searchpaths, operator, props_default={}): layout = self.layout - # hard coded to set the operators 'path' to the filename. + # hard coded to set the operators 'filepath' to the filename. import os import bpy.utils @@ -501,17 +628,26 @@ class Menu(StructRNA, _GenericUI): # collect paths files = [] - for path in searchpaths: - files.extend([(f, os.path.join(path, f)) for f in os.listdir(path)]) + for directory in searchpaths: + files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)]) files.sort() - for f, path in files: + for f, filepath in files: if f.startswith("."): continue - layout.operator(operator, text=bpy.utils.display_name(f)).path = path + preset_name = bpy.utils.display_name(f) + props = layout.operator(operator, text=preset_name) + + for attr, value in props_default.items(): + setattr(props, attr, value) + + props.filepath = filepath + if operator == "script.execute_preset": + props.menu_idname = self.bl_idname + props.preset_name = preset_name def draw_preset(self, context): """Define these on the subclass diff --git a/release/scripts/modules/console/complete_calltip.py b/release/scripts/modules/console/complete_calltip.py index c4687b4f10b..87fac9f4c07 100644 --- a/release/scripts/modules/console/complete_calltip.py +++ b/release/scripts/modules/console/complete_calltip.py @@ -124,7 +124,7 @@ def get_argspec(func, strip_self=True, doc=None, source=None): if source is None: try: source = inspect.getsource(func) - except TypeError: + except (TypeError, IOError): source = '' if source: match = re.search(DEF_SOURCE % func_name, source, RE_FLAG) diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py index 875c557f497..4b50cf6deb8 100644 --- a/release/scripts/modules/console/complete_import.py +++ b/release/scripts/modules/console/complete_import.py @@ -32,6 +32,7 @@ changes have been made: - limit list of modules to prefix in case of "from w" - sorted modules - added sphinx documentation +- complete() returns a blank list of the module isnt found """ @@ -183,3 +184,8 @@ def complete(line): if len(words) >= 3 and words[0] == 'from': mod = words[1] return filter_prefix(try_import(mod), words[-1]) + + # get here if the import is not found + # import invalidmodule + # ^, in this case return nothing + return [] diff --git a/release/scripts/modules/graphviz_export.py b/release/scripts/modules/graphviz_export.py index 1e7c7adfb7f..900d69670d5 100644 --- a/release/scripts/modules/graphviz_export.py +++ b/release/scripts/modules/graphviz_export.py @@ -51,13 +51,13 @@ def compat_str(text, line_length=0): return text -def graph_armature(obj, path, FAKE_PARENT=True, CONSTRAINTS=True, DRIVERS=True, XTRA_INFO=True): +def graph_armature(obj, filepath, FAKE_PARENT=True, CONSTRAINTS=True, DRIVERS=True, XTRA_INFO=True): CONSTRAINTS = DRIVERS = True - fileobject = open(path, "w") + fileobject = open(filepath, "w") fw = fileobject.write fw(header) - fw('label = "%s::%s" ;' % (bpy.data.filename.split("/")[-1].split("\\")[-1], obj.name)) + fw('label = "%s::%s" ;' % (bpy.data.filepath.split("/")[-1].split("\\")[-1], obj.name)) arm = obj.data @@ -157,17 +157,18 @@ def graph_armature(obj, path, FAKE_PARENT=True, CONSTRAINTS=True, DRIVERS=True, pbone = rna_path_as_pbone(rna_path) if pbone: - for target in fcurve_driver.driver.targets: - pbone_target = rna_path_as_pbone(target.data_path) - rna_path_target = target.data_path - if pbone_target: - opts = ['dir=forward', "weight=1", "arrowhead=normal", "arrowtail=none", "constraint=false", 'color="blue"', "labelfontsize=4"] # , - display_source = rna_path.replace("pose.bones", "") - display_target = rna_path_target.replace("pose.bones", "") - if XTRA_INFO: - label = "%s\\n%s" % (display_source, display_target) - opts.append('label="%s"' % compat_str(label)) - fw('"%s" -> "%s" [%s] ;\n' % (pbone_target.name, pbone.name, ','.join(opts))) + for var in fcurve_driver.driver.variables: + for target in var.targets: + pbone_target = rna_path_as_pbone(target.data_path) + rna_path_target = target.data_path + if pbone_target: + opts = ['dir=forward', "weight=1", "arrowhead=normal", "arrowtail=none", "constraint=false", 'color="blue"', "labelfontsize=4"] # , + display_source = rna_path.replace("pose.bones", "") + display_target = rna_path_target.replace("pose.bones", "") + if XTRA_INFO: + label = "%s\\n%s" % (display_source, display_target) + opts.append('label="%s"' % compat_str(label)) + fw('"%s" -> "%s" [%s] ;\n' % (pbone_target.name, pbone.name, ','.join(opts))) fw(footer) fileobject.close() @@ -177,7 +178,7 @@ def graph_armature(obj, path, FAKE_PARENT=True, CONSTRAINTS=True, DRIVERS=True, import sys sys.stdout.flush() ''' - print("\nSaved:", path) + print("\nSaved:", filepath) return True if __name__ == "__main__": diff --git a/release/scripts/modules/rigify/__init__.py b/release/scripts/modules/rigify/__init__.py index 895afd20d59..ef8f62ad058 100644 --- a/release/scripts/modules/rigify/__init__.py +++ b/release/scripts/modules/rigify/__init__.py @@ -19,17 +19,17 @@ # <pep8 compliant> import bpy -from Mathutils import Vector +from mathutils import Vector # TODO, have these in a more general module from rna_prop_ui import rna_idprop_ui_prop_get SPECIAL_TYPES = "root", LAYER_TYPES = "main", "extra", "ik", "fk" -ORG_LAYERS = [n==31 for n in range(0,32)] -MCH_LAYERS = [n==30 for n in range(0,32)] -DEF_LAYERS = [n==29 for n in range(0,32)] -ROOT_LAYERS = [n==28 for n in range(0,32)] +ORG_LAYERS = [n == 31 for n in range(0, 32)] +MCH_LAYERS = [n == 30 for n in range(0, 32)] +DEF_LAYERS = [n == 29 for n in range(0, 32)] +ROOT_LAYERS = [n == 28 for n in range(0, 32)] ORG_PREFIX = "ORG-" MCH_PREFIX = "MCH-" @@ -38,7 +38,6 @@ DEF_PREFIX = "DEF-" WGT_PREFIX = "WGT-" - class RigifyError(Exception): """Exception raised for errors in the metarig. """ @@ -235,12 +234,12 @@ def generate_rig(context, obj_orig, prefix="ORG-", META_DEF=True): bone_gen = obj.pose.bones[bone.name] # Rotation mode and transform locks - bone_gen.rotation_mode = bone.rotation_mode - bone_gen.lock_rotation = tuple(bone.lock_rotation) - bone_gen.lock_rotation_w = bone.lock_rotation_w + bone_gen.rotation_mode = bone.rotation_mode + bone_gen.lock_rotation = tuple(bone.lock_rotation) + bone_gen.lock_rotation_w = bone.lock_rotation_w bone_gen.lock_rotations_4d = bone.lock_rotations_4d - bone_gen.lock_location = tuple(bone.lock_location) - bone_gen.lock_scale = tuple(bone.lock_scale) + bone_gen.lock_location = tuple(bone.lock_location) + bone_gen.lock_scale = tuple(bone.lock_scale) # Custom properties for prop in bone.keys(): @@ -456,12 +455,12 @@ def generate_rig(context, obj_orig, prefix="ORG-", META_DEF=True): bone.deform = True else: # Assign bone appearance if there is a widget for it - obj.pose.bones[bone_name].custom_shape = context.scene.objects.get(WGT_PREFIX+bone_name) + obj.pose.bones[bone_name].custom_shape = context.scene.objects.get(WGT_PREFIX + bone_name) layer_tot[:] = [max(lay) for lay in zip(layer_tot, bone.layer)] # Only for demo'ing - layer_show = [a and not (b or c or d) for a,b,c,d in zip(layer_tot, ORG_LAYERS, MCH_LAYERS, DEF_LAYERS)] + layer_show = [a and not (b or c or d) for a, b, c, d in zip(layer_tot, ORG_LAYERS, MCH_LAYERS, DEF_LAYERS)] arm.layer = layer_show @@ -535,7 +534,7 @@ def generate_test_all(context, GRAPH=False): new_objects = rigify.generate_test(context) if GRAPH: - base_name = os.path.splitext(bpy.data.filename)[0] + base_name = os.path.splitext(bpy.data.filepath)[0] for obj, obj_new in new_objects: for obj in (obj, obj_new): fn = base_name + "-" + bpy.utils.clean_name(obj.name) diff --git a/release/scripts/modules/rigify/arm_biped.py b/release/scripts/modules/rigify/arm_biped.py index 7aa6a37304a..7a99eb5d80a 100644 --- a/release/scripts/modules/rigify/arm_biped.py +++ b/release/scripts/modules/rigify/arm_biped.py @@ -23,7 +23,7 @@ from math import radians, pi from rigify import RigifyError, ORG_PREFIX from rigify_utils import bone_class_instance, copy_bone_simple, add_pole_target_bone, add_stretch_to, blend_bone_list, get_side_name, get_base_name from rna_prop_ui import rna_idprop_ui_prop_get -from Mathutils import Vector +from mathutils import Vector METARIG_NAMES = "shoulder", "arm", "forearm", "hand" @@ -192,7 +192,7 @@ def fk(obj, definitions, base_names, options): # shoulder is used as a hinge fk_chain.rename("shoulder", "MCH-%s_hinge" % base_names[mt.arm]) - fk_chain.shoulder_e.translate(Vector(0.0, fk_chain.shoulder_e.length / 2, 0.0)) + fk_chain.shoulder_e.translate(Vector((0.0, fk_chain.shoulder_e.length / 2, 0.0))) # upper arm constrains to this. ex.socket_e = copy_bone_simple(arm, mt.arm, "MCH-%s_socket" % base_names[mt.arm]) @@ -240,11 +240,11 @@ def fk(obj, definitions, base_names, options): con.name = "hinge" con.target = obj con.subtarget = mt.shoulder - driver_fcurve = con.driver_add("influence", 0) + driver_fcurve = con.driver_add("influence") driver = driver_fcurve.driver - controller_path = fk_chain.arm_p.path_to_id() + controller_path = fk_chain.arm_p.path_from_id() # add custom prop fk_chain.arm_p["hinge"] = 0.0 prop = rna_idprop_ui_prop_get(fk_chain.arm_p, "hinge", create=True) diff --git a/release/scripts/modules/rigify/copy.py b/release/scripts/modules/rigify/copy.py index 907435da2b4..e0bff555a9b 100644 --- a/release/scripts/modules/rigify/copy.py +++ b/release/scripts/modules/rigify/copy.py @@ -110,4 +110,3 @@ def main(obj, bone_definition, base_names, options): deform(obj, bone_definition, base_names, options) return (cpy,) - diff --git a/release/scripts/modules/rigify/eye_balls.py b/release/scripts/modules/rigify/eye_balls.py index 9cb3c8229b0..48f0448dfb3 100644 --- a/release/scripts/modules/rigify/eye_balls.py +++ b/release/scripts/modules/rigify/eye_balls.py @@ -20,13 +20,92 @@ import bpy from rna_prop_ui import rna_idprop_ui_prop_get -from Mathutils import Vector +from mathutils import Vector from rigify import RigifyError from rigify_utils import copy_bone_simple #METARIG_NAMES = ("cpy",) RIG_TYPE = "eye_balls" +def addget_shape_key(obj, name="Key"): + """ Fetches a shape key, or creates it if it doesn't exist + """ + # Create a shapekey set if it doesn't already exist + if obj.data.shape_keys is None: + shape = obj.add_shape_key(name="Basis", from_mix=False) + obj.active_shape_key_index = 0 + + # Get the shapekey, or create it if it doesn't already exist + if name in obj.data.shape_keys.keys: + shape_key = obj.data.shape_keys.keys[name] + else: + shape_key = obj.add_shape_key(name=name, from_mix=False) + + return shape_key + + +def addget_shape_key_driver(obj, name="Key"): + """ Fetches the driver for the shape key, or creates it if it doesn't + already exist. + """ + driver_path = 'keys["' + name + '"].value' + fcurve = None + driver = None + new = False + if obj.data.shape_keys.animation_data is not None: + for driver_s in obj.data.shape_keys.animation_data.drivers: + if driver_s.data_path == driver_path: + fcurve = driver_s + if fcurve == None: + fcurve = obj.data.shape_keys.keys[name].driver_add("value") + fcurve.driver.type = 'AVERAGE' + new = True + + return fcurve, new + + +def create_shape_and_driver(obj, bone, meshes, shape_name, var_name, var_path, expression): + """ Creates/gets a shape key and sets up a driver for it. + + obj = armature object + bone = driving bone name + meshes = list of meshes to create the shapekey/driver on + shape_name = name of the shape key + var_name = name of the driving variable + var_path = path to the property on the bone to drive with + expression = python expression for the driver + """ + pb = obj.pose.bones + bpy.ops.object.mode_set(mode='OBJECT') + + for mesh_name in meshes: + mesh_obj = bpy.data.objects[mesh_name] + + # Add/get the shape key + shape = addget_shape_key(mesh_obj, name=shape_name) + + # Add/get the shape key driver + fcurve, a = addget_shape_key_driver(mesh_obj, name=shape_name) + + # Set up the driver + driver = fcurve.driver + driver.type = 'SCRIPTED' + driver.expression = expression + + # Get the variable, or create it if it doesn't already exist + if var_name in driver.variables: + var = driver.variables[var_name] + else: + var = driver.variables.new() + var.name = var_name + + # Set up the variable + var.type = "SINGLE_PROP" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = 'pose.bones["' + bone + '"]' + var_path + + def mark_actions(): for action in bpy.data.actions: action.tag = True @@ -120,6 +199,12 @@ def control(obj, definitions, base_names, options): head = definitions[0] eye_target = definitions[1] + # Get list of pupil mesh objects + if "mesh" in options: + pupil_meshes = options["mesh"].replace(" ", "").split(",") + else: + pupil_meshes = [] + # Get list of eyes if "eyes" in options: eye_base_names = options["eyes"].replace(" ", "").split(",") @@ -132,7 +217,7 @@ def control(obj, definitions, base_names, options): eyes += ["ORG-"+name] # Get the average position of the eyes - center = Vector(0,0,0) + center = Vector((0, 0, 0)) for eye in eyes: center += eb[eye].head if len(eyes) != 0: @@ -197,7 +282,7 @@ def control(obj, definitions, base_names, options): prop["min"] = 0.0 prop["max"] = 1.0 - free_driver_path = pb[target_ctrl].path_to_id() + '["free"]' + free_driver_path = pb[target_ctrl].path_from_id() + '["free"]' # Constraints # Mind's eye tracks eye target control @@ -210,7 +295,7 @@ def control(obj, definitions, base_names, options): con.target = obj con.subtarget = head - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' mod = fcurve.modifiers[0] @@ -241,11 +326,55 @@ def control(obj, definitions, base_names, options): con.subtarget = target_ctrl con.action = spread_action con.transform_channel = 'SCALE_X' - con.start_frame = -20 - con.end_frame = 20 + con.frame_start = -20 + con.frame_end = 20 con.minimum = 0.0 con.maximum = 2.0 con.target_space = 'LOCAL' + + + # Get/create the shape keys and drivers for pupil dilation + shape_names = ["PUPILS-dilate_wide", "PUPILS-dilate_narrow"] + slider_name = "pupil_dilate" + + # Set up the custom property on the bone + prop = rna_idprop_ui_prop_get(pb[target_ctrl], slider_name, create=True) + pb[target_ctrl][slider_name] = 0.0 + prop["min"] = 0.0 + prop["max"] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + if len(shape_names) > 1: + prop["min"] = -1.0 + prop["soft_min"] = -1.0 + + # Add the shape drivers + # Positive + if shape_names[0] != "": + # Set up the variables for creating the shape key driver + shape_name = shape_names[0] + var_name = slider_name.replace(".", "_").replace("-", "_") + var_path = '["' + slider_name + '"]' + if slider_name + "_fac" in options: + fac = options[slider_name + "_fac"] + else: + fac = 1.0 + expression = var_name + " * " + str(fac) + # Create the shape key driver + create_shape_and_driver(obj, target_ctrl, pupil_meshes, shape_name, var_name, var_path, expression) + # Negative + if shape_names[0] != "" and len(shape_names) > 1: + # Set up the variables for creating the shape key driver + shape_name = shape_names[1] + var_name = slider_name.replace(".", "_").replace("-", "_") + var_path = '["' + slider_name + '"]' + if slider_name + "_fac" in options: + fac = options[slider_name + "_fac"] + else: + fac = 1.0 + expression = var_name + " * " + str(fac) + " * -1" + # Create the shape key driver + create_shape_and_driver(obj, target_ctrl, pupil_meshes, shape_name, var_name, var_path, expression) diff --git a/release/scripts/modules/rigify/eye_lid.py b/release/scripts/modules/rigify/eye_lid.py index 39804e6b704..bb9749011c2 100644 --- a/release/scripts/modules/rigify/eye_lid.py +++ b/release/scripts/modules/rigify/eye_lid.py @@ -21,7 +21,7 @@ import bpy from rna_prop_ui import rna_idprop_ui_prop_get from math import acos -from Mathutils import Vector +from mathutils import Vector from rigify import RigifyError from rigify_utils import copy_bone_simple @@ -299,14 +299,15 @@ def control(obj, definitions, base_names, options): lid8 = copy_bone_simple(obj.data, definitions[9], base_names[definitions[9]]).name size = eb[lid1].length - eb[lid1].tail = eb[lid1].head + Vector(0,size,0) - eb[lid2].tail = eb[lid2].head + Vector(0,size,0) - eb[lid3].tail = eb[lid3].head + Vector(0,size,0) - eb[lid4].tail = eb[lid4].head + Vector(0,size,0) - eb[lid5].tail = eb[lid5].head + Vector(0,size,0) - eb[lid6].tail = eb[lid6].head + Vector(0,size,0) - eb[lid7].tail = eb[lid7].head + Vector(0,size,0) - eb[lid8].tail = eb[lid8].head + Vector(0,size,0) + size_y = Vector(0.0, size, 0.0) + eb[lid1].tail = eb[lid1].head + size_y + eb[lid2].tail = eb[lid2].head + size_y + eb[lid3].tail = eb[lid3].head + size_y + eb[lid4].tail = eb[lid4].head + size_y + eb[lid5].tail = eb[lid5].head + size_y + eb[lid6].tail = eb[lid6].head + size_y + eb[lid7].tail = eb[lid7].head + size_y + eb[lid8].tail = eb[lid8].head + size_y eb[lid1].roll = 0 eb[lid2].roll = 0 @@ -355,7 +356,7 @@ def control(obj, definitions, base_names, options): prop["min"] = 0.0 prop["max"] = 1.0 - close_driver_path = pb[upper_lid_ctrl].path_to_id() + '["close_action"]' + close_driver_path = pb[upper_lid_ctrl].path_from_id() + '["close_action"]' # Constraints @@ -433,12 +434,12 @@ def control(obj, definitions, base_names, options): con.subtarget = upper_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance*2 con.maximum = distance con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -452,12 +453,12 @@ def control(obj, definitions, base_names, options): con.subtarget = upper_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance*2 con.maximum = distance con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -470,12 +471,12 @@ def control(obj, definitions, base_names, options): con.subtarget = upper_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance*2 con.maximum = distance con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -488,12 +489,12 @@ def control(obj, definitions, base_names, options): con.subtarget = upper_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance*2 con.maximum = distance con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -506,12 +507,12 @@ def control(obj, definitions, base_names, options): con.subtarget = upper_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance*2 con.maximum = distance con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -525,12 +526,12 @@ def control(obj, definitions, base_names, options): con.subtarget = lower_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance con.maximum = distance*2 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -543,12 +544,12 @@ def control(obj, definitions, base_names, options): con.subtarget = lower_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance con.maximum = distance*2 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -561,12 +562,12 @@ def control(obj, definitions, base_names, options): con.subtarget = lower_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance con.maximum = distance*2 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -579,12 +580,12 @@ def control(obj, definitions, base_names, options): con.subtarget = lower_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance con.maximum = distance*2 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -597,12 +598,12 @@ def control(obj, definitions, base_names, options): con.subtarget = lower_lid_ctrl con.action = close_action con.transform_channel = 'LOCATION_Y' - con.start_frame = -30 - con.end_frame = 30 + con.frame_start = -30 + con.frame_end = 30 con.minimum = -distance con.maximum = distance*2 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() diff --git a/release/scripts/modules/rigify/finger_curl.py b/release/scripts/modules/rigify/finger_curl.py index f6574fc75bb..63f1816012b 100644 --- a/release/scripts/modules/rigify/finger_curl.py +++ b/release/scripts/modules/rigify/finger_curl.py @@ -57,31 +57,20 @@ def metarig_template(): def metarig_definition(obj, orig_bone_name): ''' The bone given is the first in a chain - Expects a chain of at least 2 children. + Expects a chain with at least 1 child of the same base name. eg. - finger -> finger_01 -> finger_02 + finger_01 -> finger_02 ''' - bone_definition = [] - orig_bone = obj.data.bones[orig_bone_name] - bone_definition.append(orig_bone.name) - - bone = orig_bone - chain = 0 - while chain < 2: # first 2 bones only have 1 child - children = bone.children - - if len(children) != 1: - raise RigifyError("expected the chain to have 2 children from bone '%s' without a fork" % orig_bone_name) - bone = children[0] - bone_definition.append(bone.name) # finger_02, finger_03 - chain += 1 - - if len(bone_definition) != len(METARIG_NAMES): - raise RigifyError("internal problem, expected %d bones" % len(METARIG_NAMES)) - + bone_definition = [orig_bone.name] + + bone_definition.extend([child.name for child in orig_bone.children_recursive_basename]) + + if len(bone_definition) < 2: + raise RigifyError("expected the chain to have at least 1 child from bone '%s' without the same base name" % orig_bone_name) + return bone_definition @@ -90,6 +79,8 @@ def deform(obj, definitions, base_names, options): """ bpy.ops.object.mode_set(mode='EDIT') + three_digits = True if len(definitions) > 2 else False + # Create base digit bones: two bones, each half of the base digit. f1a = copy_bone_simple(obj.data, definitions[0], "DEF-%s.01" % base_names[definitions[0]], parent=True) f1b = copy_bone_simple(obj.data, definitions[0], "DEF-%s.02" % base_names[definitions[0]], parent=True) @@ -102,13 +93,15 @@ def deform(obj, definitions, base_names, options): # Create the other deform bones. f2 = copy_bone_simple(obj.data, definitions[1], "DEF-%s" % base_names[definitions[1]], parent=True) - f3 = copy_bone_simple(obj.data, definitions[2], "DEF-%s" % base_names[definitions[2]], parent=True) + if three_digits: + f3 = copy_bone_simple(obj.data, definitions[2], "DEF-%s" % base_names[definitions[2]], parent=True) # Store names before leaving edit mode f1a_name = f1a.name f1b_name = f1b.name f2_name = f2.name - f3_name = f3.name + if three_digits: + f3_name = f3.name # Leave edit mode bpy.ops.object.mode_set(mode='OBJECT') @@ -117,7 +110,8 @@ def deform(obj, definitions, base_names, options): f1a = obj.pose.bones[f1a_name] f1b = obj.pose.bones[f1b_name] f2 = obj.pose.bones[f2_name] - f3 = obj.pose.bones[f3_name] + if three_digits: + f3 = obj.pose.bones[f3_name] # Constrain the base digit's bones con = f1a.constraints.new('DAMPED_TRACK') @@ -141,15 +135,18 @@ def deform(obj, definitions, base_names, options): con.target = obj con.subtarget = definitions[1] - con = f3.constraints.new('COPY_TRANSFORMS') - con.name = "copy_transforms" - con.target = obj - con.subtarget = definitions[2] + if three_digits: + con = f3.constraints.new('COPY_TRANSFORMS') + con.name = "copy_transforms" + con.target = obj + con.subtarget = definitions[2] def main(obj, bone_definition, base_names, options): # *** EDITMODE bpy.ops.object.mode_set(mode='EDIT') + + three_digits = True if len(bone_definition) > 2 else False # get assosiated data arm = obj.data @@ -159,7 +156,8 @@ def main(obj, bone_definition, base_names, options): org_f1 = bone_definition[0] # Original finger bone 01 org_f2 = bone_definition[1] # Original finger bone 02 - org_f3 = bone_definition[2] # Original finger bone 03 + if three_digits: + org_f3 = bone_definition[2] # Original finger bone 03 # Check options if "bend_ratio" in options: @@ -179,7 +177,10 @@ def main(obj, bone_definition, base_names, options): # Create the control bone base_name = base_names[bone_definition[0]].split(".", 1)[0] - tot_len = eb[org_f1].length + eb[org_f2].length + eb[org_f3].length + if three_digits: + tot_len = eb[org_f1].length + eb[org_f2].length + eb[org_f3].length + else: + tot_len = eb[org_f1].length + eb[org_f2].length control = copy_bone_simple(arm, bone_definition[0], base_name + get_side_name(base_names[bone_definition[0]]), parent=True).name eb[control].connected = eb[org_f1].connected eb[control].parent = eb[org_f1].parent @@ -188,26 +189,30 @@ def main(obj, bone_definition, base_names, options): # Create secondary control bones f1 = copy_bone_simple(arm, bone_definition[0], base_names[bone_definition[0]]).name f2 = copy_bone_simple(arm, bone_definition[1], base_names[bone_definition[1]]).name - f3 = copy_bone_simple(arm, bone_definition[2], base_names[bone_definition[2]]).name + if three_digits: + f3 = copy_bone_simple(arm, bone_definition[2], base_names[bone_definition[2]]).name # Create driver bones df1 = copy_bone_simple(arm, bone_definition[0], "MCH-" + base_names[bone_definition[0]]).name eb[df1].length /= 2 df2 = copy_bone_simple(arm, bone_definition[1], "MCH-" + base_names[bone_definition[1]]).name eb[df2].length /= 2 - df3 = copy_bone_simple(arm, bone_definition[2], "MCH-" + base_names[bone_definition[2]]).name - eb[df3].length /= 2 + if three_digits: + df3 = copy_bone_simple(arm, bone_definition[2], "MCH-" + base_names[bone_definition[2]]).name + eb[df3].length /= 2 # Set parents of the bones, interleaving the driver bones with the secondary control bones - eb[f3].connected = False - eb[df3].connected = False + if three_digits: + eb[f3].connected = False + eb[df3].connected = False eb[f2].connected = False eb[df2].connected = False eb[f1].connected = False eb[df1].connected = eb[org_f1].connected - eb[f3].parent = eb[df3] - eb[df3].parent = eb[f2] + if three_digits: + eb[f3].parent = eb[df3] + eb[df3].parent = eb[f2] eb[f2].parent = eb[df2] eb[df2].parent = eb[f1] eb[f1].parent = eb[df1] @@ -234,12 +239,15 @@ def main(obj, bone_definition, base_names, options): pb[control].lock_scale = True, False, True pb[f1].rotation_mode = 'YZX' pb[f2].rotation_mode = 'YZX' - pb[f3].rotation_mode = 'YZX' + if three_digits: + pb[f3].rotation_mode = 'YZX' pb[f1].lock_location = True, True, True pb[f2].lock_location = True, True, True - pb[f3].lock_location = True, True, True + if three_digits: + pb[f3].lock_location = True, True, True pb[df2].rotation_mode = 'YZX' - pb[df3].rotation_mode = 'YZX' + if three_digits: + pb[df3].rotation_mode = 'YZX' # Add the bend_ratio property to the control bone pb[control]["bend_ratio"] = bend_ratio @@ -271,18 +279,19 @@ def main(obj, bone_definition, base_names, options): con.target = obj con.subtarget = f2 - con = pb[org_f3].constraints.new('COPY_TRANSFORMS') - con.target = obj - con.subtarget = f3 + if three_digits: + con = pb[org_f3].constraints.new('COPY_TRANSFORMS') + con.target = obj + con.subtarget = f3 if make_hinge: con = pb[hinge].constraints.new('COPY_TRANSFORMS') con.target = obj con.subtarget = bb[org_f1].parent.name - hinge_driver_path = pb[control].path_to_id() + '["hinge"]' + hinge_driver_path = pb[control].path_from_id() + '["hinge"]' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -301,10 +310,15 @@ def main(obj, bone_definition, base_names, options): con.subtarget = socket # Create the drivers for the driver bones (control bone scale rotates driver bones) - controller_path = pb[control].path_to_id() # 'pose.bones["%s"]' % control_bone_name + controller_path = pb[control].path_from_id() # 'pose.bones["%s"]' % control_bone_name + + if three_digits: + finger_digits = [df2, df3] + else: + finger_digits = [df2] i = 0 - for bone in [df2, df3]: + for bone in finger_digits: # XXX - todo, any number if i == 2: @@ -334,10 +348,13 @@ def main(obj, bone_definition, base_names, options): var.targets[0].data_path = controller_path + '["bend_ratio"]' # XXX - todo, any number - if i == 0: - driver.expression = '(-scale+1.0)*pi*2.0*(1.0-br)' - elif i == 1: - driver.expression = '(-scale+1.0)*pi*2.0*br' + if three_digits: + if i == 0: + driver.expression = '(-scale+1.0)*pi*2.0*(1.0-br)' + elif i == 1: + driver.expression = '(-scale+1.0)*pi*2.0*br' + else: + driver.expression = driver.expression = '(-scale+1.0)*pi*2.0' i += 1 @@ -346,11 +363,16 @@ def main(obj, bone_definition, base_names, options): layer = [n==options["ex_layer"] for n in range(0,32)] else: layer = list(arm.bones[bone_definition[0]].layer) - for bone_name in [f1, f2, f3]: - arm.bones[bone_name].layer = layer + #for bone_name in [f1, f2, f3]: + # arm.bones[bone_name].layer = layer + arm.bones[f1].layer = layer + arm.bones[f2].layer = layer + if three_digits: + arm.bones[f3].layer = layer layer = list(arm.bones[bone_definition[0]].layer) bb[control].layer = layer # no blending the result of this return None + diff --git a/release/scripts/modules/rigify/leg_biped.py b/release/scripts/modules/rigify/leg_biped.py index 40d2ea0c4f2..53028986873 100644 --- a/release/scripts/modules/rigify/leg_biped.py +++ b/release/scripts/modules/rigify/leg_biped.py @@ -201,7 +201,7 @@ def ik(obj, bone_definition, base_names, options): ik.knee_target_e.local_location = False # roll the bone to point up... could also point in the same direction as ik.foot_roll - # ik.foot_roll_02_e.matrix * Vector(0.0, 0.0, 1.0) # ACK!, no rest matrix in editmode + # ik.foot_roll_02_e.matrix * Vector((0.0, 0.0, 1.0)) # ACK!, no rest matrix in editmode ik.foot_roll_01_e.align_roll((0.0, 0.0, -1.0)) bpy.ops.object.mode_set(mode='OBJECT') @@ -227,7 +227,7 @@ def ik(obj, bone_definition, base_names, options): con = ik_chain.shin_p.constraints.new('IK') con.chain_length = 2 con.iterations = 500 - con.pole_angle = -pi/2 + con.pole_angle = -pi / 2.0 con.use_tail = True con.use_stretch = True con.use_target = True @@ -265,7 +265,7 @@ def ik(obj, bone_definition, base_names, options): # last step setup layers if "ik_layer" in options: - layer = [n==options["ik_layer"] for n in range(0,32)] + layer = [n == options["ik_layer"] for n in range(0, 32)] else: layer = list(mt_chain.thigh_b.layer) for attr in ik_chain.attr_names: @@ -279,7 +279,7 @@ def ik(obj, bone_definition, base_names, options): def fk(obj, bone_definition, base_names, options): - from Mathutils import Vector + from mathutils import Vector arm = obj.data # these account for all bones in METARIG_NAMES @@ -298,7 +298,7 @@ def fk(obj, bone_definition, base_names, options): ex.thigh_socket_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_socket" % base_names[mt_chain.thigh], parent=True) ex.thigh_socket = ex.thigh_socket_e.name - ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector(0.0, 0.0, ex.thigh_socket_e.length / 4.0) + ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector((0.0, 0.0, ex.thigh_socket_e.length / 4.0)) ex.thigh_hinge_e = copy_bone_simple(arm, mt.hips, "MCH-%s_hinge" % base_names[mt_chain.thigh], parent=False) ex.thigh_hinge = ex.thigh_hinge_e.name @@ -349,9 +349,9 @@ def fk(obj, bone_definition, base_names, options): con.subtarget = mt.hips # add driver - hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]' + hinge_driver_path = fk_chain.thigh_p.path_from_id() + '["hinge"]' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -368,7 +368,7 @@ def fk(obj, bone_definition, base_names, options): # last step setup layers if "fk_layer" in options: - layer = [n==options["fk_layer"] for n in range(0,32)] + layer = [n == options["fk_layer"] for n in range(0, 32)] else: layer = list(mt_chain.thigh_b.layer) for attr in fk_chain.attr_names: @@ -499,4 +499,3 @@ def main(obj, bone_definition, base_names, options): bpy.ops.object.mode_set(mode='OBJECT') blend_bone_list(obj, bone_definition + [None], bones_fk, bones_ik, target_bone=bones_ik[6], target_prop="ik", blend_default=1.0) - diff --git a/release/scripts/modules/rigify/leg_quadruped.py b/release/scripts/modules/rigify/leg_quadruped.py index c21680740bd..688387bbd53 100644 --- a/release/scripts/modules/rigify/leg_quadruped.py +++ b/release/scripts/modules/rigify/leg_quadruped.py @@ -23,7 +23,7 @@ from rna_prop_ui import rna_idprop_ui_prop_get from math import pi from rigify import RigifyError from rigify_utils import bone_class_instance, copy_bone_simple, get_side_name, get_base_name -from Mathutils import Vector +from mathutils import Vector METARIG_NAMES = "hips", "thigh", "shin", "foot", "toe" @@ -160,7 +160,7 @@ def ik(obj, bone_definition, base_names, options): ik.foot_roll_e.parent = ik_chain.foot_e ik.foot_roll_e.head -= mt_chain.toe_e.vector.normalize() * mt_chain.foot_e.length ik.foot_roll_e.tail = ik.foot_roll_e.head - (mt_chain.foot_e.vector.normalize() * mt_chain.toe_e.length) - ik.foot_roll_e.align_roll(mt_chain.foot_e.matrix.rotation_part() * Vector(0.0, 0.0, -1.0)) + ik.foot_roll_e.align_roll(mt_chain.foot_e.matrix.rotation_part() * Vector((0.0, 0.0, -1.0))) # MCH-foot ik.foot_roll_01_e = copy_bone_simple(arm, mt_chain.foot, "MCH-" + base_names[mt_chain.foot]) @@ -207,13 +207,13 @@ def ik(obj, bone_definition, base_names, options): prop["min"] = 0.0 prop["max"] = 1.0 - ik_driver_path = pb[ik_chain.foot].path_to_id() + '["ik"]' + ik_driver_path = pb[ik_chain.foot].path_from_id() + '["ik"]' # simple constraining of orig bones con = mt_chain.thigh_p.constraints.new('COPY_TRANSFORMS') con.target = obj con.subtarget = ik_chain.thigh - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -225,7 +225,7 @@ def ik(obj, bone_definition, base_names, options): con = mt_chain.shin_p.constraints.new('COPY_TRANSFORMS') con.target = obj con.subtarget = ik_chain.shin - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -237,7 +237,7 @@ def ik(obj, bone_definition, base_names, options): con = mt_chain.foot_p.constraints.new('COPY_TRANSFORMS') con.target = obj con.subtarget = ik.foot_roll_02 - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -249,7 +249,7 @@ def ik(obj, bone_definition, base_names, options): con = mt_chain.toe_p.constraints.new('COPY_TRANSFORMS') con.target = obj con.subtarget = ik_chain.toe - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -369,9 +369,9 @@ def fk(obj, bone_definition, base_names, options): prop["min"] = 0.0 prop["max"] = 1.0 - hinge_driver_path = pb[fk_chain.thigh].path_to_id() + '["hinge"]' + hinge_driver_path = pb[fk_chain.thigh].path_from_id() + '["hinge"]' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' diff --git a/release/scripts/modules/rigify/mouth.py b/release/scripts/modules/rigify/mouth.py index 31d7c5a1ce9..d516a48ec95 100644 --- a/release/scripts/modules/rigify/mouth.py +++ b/release/scripts/modules/rigify/mouth.py @@ -21,7 +21,7 @@ import bpy from rna_prop_ui import rna_idprop_ui_prop_get from math import acos, pi -from Mathutils import Vector +from mathutils import Vector from rigify import RigifyError from rigify_utils import copy_bone_simple @@ -77,7 +77,7 @@ def addget_shape_key_driver(obj, name="Key"): if driver_s.data_path == driver_path: fcurve = driver_s if fcurve == None: - fcurve = obj.data.shape_keys.keys[name].driver_add("value", 0) + fcurve = obj.data.shape_keys.keys[name].driver_add("value") fcurve.driver.type = 'AVERAGE' new = True @@ -173,7 +173,7 @@ def deform(obj, definitions, base_names, options): jopen1 = copy_bone_simple(obj.data, jaw, "MCH-"+base_names[jaw]+".track1", parent=True).name eb[jopen1].connected = False eb[jopen1].head = eb[jaw].tail - eb[jopen1].tail = eb[jopen1].head + Vector(0, 0, eb[jaw].length/4) + eb[jopen1].tail = eb[jopen1].head + Vector((0, 0, eb[jaw].length/4)) jopen2 = copy_bone_simple(obj.data, jopen1, "MCH-"+base_names[jaw]+".track2").name eb[jopen2].parent = eb[jaw] @@ -427,7 +427,7 @@ def control(obj, definitions, base_names, options): # Jaw open tracker jopent = copy_bone_simple(obj.data, jaw_e.name, "MCH-"+base_names[jaw_e.name]+".track", parent=True).name eb[jopent].connected = False - eb[jopent].tail = jaw_e.tail + Vector(0,0,jaw_e.length) + eb[jopent].tail = jaw_e.tail + Vector((0.0, 0.0, jaw_e.length)) eb[jopent].head = jaw_e.tail bpy.ops.object.mode_set(mode='OBJECT') @@ -448,7 +448,7 @@ def control(obj, definitions, base_names, options): prop["min"] = 0.0 prop["max"] = 1.0 - open_driver_path = pb[lip1].path_to_id() + '["open_action"]' + open_driver_path = pb[lip1].path_from_id() + '["open_action"]' # Constraints @@ -543,12 +543,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -561,12 +561,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -579,12 +579,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -597,12 +597,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -615,12 +615,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -633,12 +633,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -651,12 +651,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -669,12 +669,12 @@ def control(obj, definitions, base_names, options): con.subtarget = jopent con.action = open_action con.transform_channel = 'SCALE_Y' - con.start_frame = 0 - con.end_frame = 60 + con.frame_start = 0 + con.frame_end = 60 con.minimum = 0.0 con.maximum = 1.0 con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() diff --git a/release/scripts/modules/rigify/neck.py b/release/scripts/modules/rigify/neck.py new file mode 100644 index 00000000000..0d2ab25bcc9 --- /dev/null +++ b/release/scripts/modules/rigify/neck.py @@ -0,0 +1,344 @@ +# ##### 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 rigify import RigifyError +from rigify_utils import bone_class_instance, copy_bone_simple +from rna_prop_ui import rna_idprop_ui_prop_get + + + +def metarig_template(): + # TODO: + ## generated by rigify.write_meta_rig + #bpy.ops.object.mode_set(mode='EDIT') + #obj = bpy.context.active_object + #arm = obj.data + #bone = arm.edit_bones.new('body') + #bone.head[:] = 0.0000, -0.0276, -0.1328 + #bone.tail[:] = 0.0000, -0.0170, -0.0197 + #bone.roll = 0.0000 + #bone.connected = False + #bone = arm.edit_bones.new('head') + #bone.head[:] = 0.0000, -0.0170, -0.0197 + #bone.tail[:] = 0.0000, 0.0726, 0.1354 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['body'] + #bone = arm.edit_bones.new('neck.01') + #bone.head[:] = 0.0000, -0.0170, -0.0197 + #bone.tail[:] = 0.0000, -0.0099, 0.0146 + #bone.roll = 0.0000 + #bone.connected = False + #bone.parent = arm.edit_bones['head'] + #bone = arm.edit_bones.new('neck.02') + #bone.head[:] = 0.0000, -0.0099, 0.0146 + #bone.tail[:] = 0.0000, -0.0242, 0.0514 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.01'] + #bone = arm.edit_bones.new('neck.03') + #bone.head[:] = 0.0000, -0.0242, 0.0514 + #bone.tail[:] = 0.0000, -0.0417, 0.0868 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.02'] + #bone = arm.edit_bones.new('neck.04') + #bone.head[:] = 0.0000, -0.0417, 0.0868 + #bone.tail[:] = 0.0000, -0.0509, 0.1190 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.03'] + #bone = arm.edit_bones.new('neck.05') + #bone.head[:] = 0.0000, -0.0509, 0.1190 + #bone.tail[:] = 0.0000, -0.0537, 0.1600 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.04'] + # + #bpy.ops.object.mode_set(mode='OBJECT') + #pbone = obj.pose.bones['head'] + #pbone['type'] = 'neck_flex' + pass + + +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is neck_01, its parent is the body + eg. + body -> neck_01 -> neck_02 -> neck_03.... etc + ''' + arm = obj.data + neck = arm.bones[orig_bone_name] + body = neck.parent + + bone_definition = [body.name, neck.name] + bone_definition.extend([child.name for child in neck.children_recursive_basename]) + return bone_definition + + +def deform(obj, definitions, base_names, options): + for org_bone_name in definitions[1:]: + bpy.ops.object.mode_set(mode='EDIT') + + # Create deform bone. + bone = copy_bone_simple(obj.data, org_bone_name, "DEF-%s" % base_names[org_bone_name], parent=True) + + # Store name before leaving edit mode + bone_name = bone.name + + # Leave edit mode + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bone + bone = obj.pose.bones[bone_name] + + # Constrain to the original bone + # XXX. Todo, is this needed if the bone is connected to its parent? + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_loc" + con.target = obj + con.subtarget = org_bone_name + + +def main(obj, bone_definition, base_names, options): + from mathutils import Vector + + arm = obj.data + eb = obj.data.edit_bones + bb = obj.data.bones + pb = obj.pose.bones + + body = bone_definition[0] + + # Create the neck and head control bones + if "head_name" in options: + head_name = options["head_name"] + else: + head_name = "head" + + neck_name = base_names[bone_definition[1]].split(".")[0] + + neck_ctrl = copy_bone_simple(arm, bone_definition[1], neck_name).name + head_ctrl = copy_bone_simple(arm, bone_definition[len(bone_definition)-1], head_name).name + eb[head_ctrl].tail += eb[neck_ctrl].head - eb[head_ctrl].head + eb[head_ctrl].head = eb[neck_ctrl].head + + # Create hinge and socket bones + neck_hinge = copy_bone_simple(arm, bone_definition[0], "MCH-" + neck_name + "_hinge").name + head_hinge = copy_bone_simple(arm, neck_ctrl, "MCH-" + head_name + "_hinge").name + eb[neck_hinge].tail += eb[neck_ctrl].head - eb[neck_hinge].head + eb[neck_hinge].head = eb[neck_ctrl].head + eb[head_hinge].tail += eb[neck_ctrl].head - eb[head_hinge].head + eb[head_hinge].head = eb[neck_ctrl].head + + neck_socket = copy_bone_simple(arm, bone_definition[1], "MCH-" + neck_name + "_socket").name + head_socket = copy_bone_simple(arm, bone_definition[1], "MCH-" + head_name + "_socket").name + + # Parent-child relationships between the body, hinges, controls, and sockets + eb[neck_ctrl].parent = eb[neck_hinge] + eb[head_ctrl].parent = eb[head_hinge] + + eb[neck_socket].parent = eb[body] + eb[head_socket].parent = eb[body] + + # Create neck bones + neck = [] # neck bones + neck_neck = [] # bones constrained to neck control + neck_head = [] # bones constrained to head control + for i in range(1, len(bone_definition)): + # Create bones + neck_bone = copy_bone_simple(arm, bone_definition[i], base_names[bone_definition[i]]).name + neck_neck_bone = copy_bone_simple(arm, neck_ctrl, "MCH-" + base_names[bone_definition[i]] + ".neck").name + neck_head_bone = copy_bone_simple(arm, head_ctrl, "MCH-" + base_names[bone_definition[i]] + ".head").name + + # Move them all to the same place + eb[neck_neck_bone].tail += eb[neck_bone].head - eb[neck_neck_bone].head + eb[neck_head_bone].tail += eb[neck_bone].head - eb[neck_neck_bone].head + eb[neck_neck_bone].head = eb[neck_bone].head + eb[neck_head_bone].head = eb[neck_bone].head + + # Parent/child relationships + eb[neck_bone].parent = eb[neck_head_bone] + eb[neck_head_bone].parent = eb[neck_neck_bone] + + if i > 1: + eb[neck_neck_bone].parent = eb[neck[i-2]] + else: + eb[neck_neck_bone].parent = eb[body] + + # Add them to the lists + neck += [neck_bone] + neck_neck += [neck_neck_bone] + neck_head += [neck_head_bone] + + # Create deformation rig + deform(obj, bone_definition, base_names, options) + + + bpy.ops.object.mode_set(mode='OBJECT') + + # Axis locks + pb[neck_ctrl].lock_location = True, True, True + pb[head_ctrl].lock_location = True, True, True + + for bone in neck: + pb[bone].lock_location = True, True, True + + # Neck hinge + prop = rna_idprop_ui_prop_get(pb[neck_ctrl], "hinge", create=True) + pb[neck_ctrl]["hinge"] = 0.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + prop["hard_min"] = 0.0 + prop["hard_max"] = 1.0 + + con = pb[neck_hinge].constraints.new('COPY_LOCATION') + con.name = "socket" + con.target = obj + con.subtarget = neck_socket + + con = pb[neck_hinge].constraints.new('COPY_ROTATION') + con.name = "hinge" + con.target = obj + con.subtarget = body + + hinge_driver_path = pb[neck_ctrl].path_from_id() + '["hinge"]' + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = hinge_driver_path + + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + # Head hinge + prop = rna_idprop_ui_prop_get(pb[head_ctrl], "hinge", create=True) + pb[head_ctrl]["hinge"] = 0.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + prop["hard_min"] = 0.0 + prop["hard_max"] = 1.0 + + con = pb[head_hinge].constraints.new('COPY_LOCATION') + con.name = "socket" + con.target = obj + con.subtarget = head_socket + + con = pb[head_hinge].constraints.new('COPY_ROTATION') + con.name = "hinge" + con.target = obj + con.subtarget = neck_ctrl + + hinge_driver_path = pb[head_ctrl].path_from_id() + '["hinge"]' + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = hinge_driver_path + + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + # Neck rotation constraints + for i in range(0, len(neck_neck)): + con = pb[neck_neck[i]].constraints.new('COPY_ROTATION') + con.name = "neck rotation" + con.target = obj + con.subtarget = neck_ctrl + con.influence = (i+1) / len(neck_neck) + + + # Head rotation constraints/drivers + prop = rna_idprop_ui_prop_get(pb[head_ctrl], "extent", create=True) + if "extent" in options: + pb[head_ctrl]["extent"] = options["extent"] + else: + pb[head_ctrl]["extent"] = 0.5 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + prop["hard_min"] = 0.0 + prop["hard_max"] = 1.0 + + extent_prop_path = pb[head_ctrl].path_from_id() + '["extent"]' + + for i in range(0, len(neck_head)): + con = pb[neck_head[i]].constraints.new('COPY_ROTATION') + con.name = "head rotation" + con.target = obj + con.subtarget = head_ctrl + + if i < (len(neck_head)-1): + inf = (i+1) / len(neck_head) + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + var.name = "ext" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = extent_prop_path + + driver.expression = "0 if ext == 0 else (((%s-1)/ext)+1)" % inf + else: + con.influence = 1.0 + + # Constrain original bones to the neck bones + for i in range(0, len(neck)): + con = pb[bone_definition[i+1]].constraints.new('COPY_TRANSFORMS') + con.name = "copy_transform" + con.target = obj + con.subtarget = neck[i] + + + # Set the controls' custom shapes to use other bones for transforms + pb[neck_ctrl].custom_shape_transform = pb[bone_definition[len(bone_definition)//2]] + pb[head_ctrl].custom_shape_transform = pb[bone_definition[len(bone_definition)-1]] + + + # last step setup layers + if "ex_layer" in options: + layer = [n==options["ex_layer"] for n in range(0,32)] + else: + layer = list(arm.bones[bone_definition[1]].layer) + for bone in neck: + bb[bone].layer = layer + + layer = list(arm.bones[bone_definition[1]].layer) + bb[neck_ctrl].layer = layer + bb[head_ctrl].layer = layer + + + # no blending the result of this + return None + diff --git a/release/scripts/modules/rigify/neck_flex.py b/release/scripts/modules/rigify/neck_flex.py index b3628ef4c2c..08f963434d1 100644 --- a/release/scripts/modules/rigify/neck_flex.py +++ b/release/scripts/modules/rigify/neck_flex.py @@ -125,7 +125,7 @@ def deform(obj, definitions, base_names, options): def main(obj, bone_definition, base_names, options): - from Mathutils import Vector + from mathutils import Vector arm = obj.data @@ -176,7 +176,7 @@ def main(obj, bone_definition, base_names, options): ex.neck_socket_e.connected = False ex.neck_socket_e.parent = mt.body_e ex.neck_socket_e.head = mt.head_e.head - ex.neck_socket_e.tail = mt.head_e.head - Vector(0.0, neck_chain_segment_length / 2.0, 0.0) + ex.neck_socket_e.tail = mt.head_e.head - Vector((0.0, neck_chain_segment_length / 2.0, 0.0)) ex.neck_socket_e.roll = 0.0 @@ -237,9 +237,9 @@ def main(obj, bone_definition, base_names, options): con.subtarget = mt.body # add driver - hinge_driver_path = ex.head_ctrl_p.path_to_id() + '["hinge"]' + hinge_driver_path = ex.head_ctrl_p.path_from_id() + '["hinge"]' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -254,12 +254,12 @@ def main(obj, bone_definition, base_names, options): mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 - head_driver_path = ex.head_ctrl_p.path_to_id() + head_driver_path = ex.head_ctrl_p.path_from_id() target_names = [("b%.2d" % (i + 1)) for i in range(len(neck_chain))] ex.head_ctrl_p["bend_tot"] = 0.0 - fcurve = ex.head_ctrl_p.driver_add('["bend_tot"]', 0) + fcurve = ex.head_ctrl_p.driver_add('["bend_tot"]') driver = fcurve.driver driver.type = 'SUM' fcurve.modifiers.remove(0) # grr dont need a modifier @@ -296,7 +296,7 @@ def main(obj, bone_definition, base_names, options): con.owner_space = 'LOCAL' con.target_space = 'LOCAL' - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'SCRIPTED' driver.expression = "bend/bend_tot" @@ -332,7 +332,7 @@ def main(obj, bone_definition, base_names, options): # last step setup layers if "ex_layer" in options: - layer = [n==options["ex_layer"] for n in range(0,32)] + layer = [n == options["ex_layer"] for n in range(0, 32)] else: layer = list(arm.bones[bone_definition[1]].layer) for attr in ex_chain.attr_names: @@ -346,4 +346,3 @@ def main(obj, bone_definition, base_names, options): # no blending the result of this return None - diff --git a/release/scripts/modules/rigify/palm_curl.py b/release/scripts/modules/rigify/palm_curl.py index 6626438709d..2bdd28a348a 100644 --- a/release/scripts/modules/rigify/palm_curl.py +++ b/release/scripts/modules/rigify/palm_curl.py @@ -156,7 +156,7 @@ def main(obj, bone_definition, base_names, options): driver_fcurves = pinky_pbone.driver_add("rotation_euler") - controller_path = control_pbone.path_to_id() + controller_path = control_pbone.path_from_id() # add custom prop control_pbone["spread"] = 0.0 @@ -248,13 +248,13 @@ def main(obj, bone_definition, base_names, options): # NOTE: the direction of the Z rotation depends on which side the palm is on. # we could do a simple side-of-x test but better to work out the direction # the hand is facing. - from Mathutils import Vector + from mathutils import Vector from math import degrees child_pbone_01 = obj.pose.bones[children[0]].bone child_pbone_02 = obj.pose.bones[children[1]].bone rel_vec = child_pbone_01.head - child_pbone_02.head - x_vec = child_pbone_01.matrix.rotation_part() * Vector(1.0, 0.0, 0.0) + x_vec = child_pbone_01.matrix.rotation_part() * Vector((1.0, 0.0, 0.0)) return degrees(rel_vec.angle(x_vec)) > 90.0 diff --git a/release/scripts/modules/rigify/shape_key_control.py b/release/scripts/modules/rigify/shape_key_control.py index fd0e900a7b5..ac3987ca7e1 100644 --- a/release/scripts/modules/rigify/shape_key_control.py +++ b/release/scripts/modules/rigify/shape_key_control.py @@ -57,7 +57,7 @@ def addget_shape_key_driver(obj, name="Key"): if driver_s.data_path == driver_path: fcurve = driver_s if fcurve == None: - fcurve = obj.data.shape_keys.keys[name].driver_add("value", 0) + fcurve = obj.data.shape_keys.keys[name].driver_add("value") fcurve.driver.type = 'AVERAGE' new = True diff --git a/release/scripts/modules/rigify/shape_key_distance.py b/release/scripts/modules/rigify/shape_key_distance.py index 68501ac829b..06dd3d67d4b 100644 --- a/release/scripts/modules/rigify/shape_key_distance.py +++ b/release/scripts/modules/rigify/shape_key_distance.py @@ -54,7 +54,7 @@ def addget_shape_key_driver(obj, name="Key"): if driver_s.data_path == driver_path: fcurve = driver_s if fcurve == None: - fcurve = obj.data.shape_keys.keys[name].driver_add("value", 0) + fcurve = obj.data.shape_keys.keys[name].driver_add("value") fcurve.driver.type = 'AVERAGE' return fcurve diff --git a/release/scripts/modules/rigify/shape_key_rotdiff.py b/release/scripts/modules/rigify/shape_key_rotdiff.py index 2c30d95e666..7049bcf74bb 100644 --- a/release/scripts/modules/rigify/shape_key_rotdiff.py +++ b/release/scripts/modules/rigify/shape_key_rotdiff.py @@ -54,7 +54,7 @@ def addget_shape_key_driver(obj, name="Key"): if driver_s.data_path == driver_path: fcurve = driver_s if fcurve == None: - fcurve = obj.data.shape_keys.keys[name].driver_add("value", 0) + fcurve = obj.data.shape_keys.keys[name].driver_add("value") fcurve.driver.type = 'AVERAGE' return fcurve diff --git a/release/scripts/modules/rigify/spine_pivot_flex.py b/release/scripts/modules/rigify/spine_pivot_flex.py index 3ae5818f908..d3bab72d9a1 100644 --- a/release/scripts/modules/rigify/spine_pivot_flex.py +++ b/release/scripts/modules/rigify/spine_pivot_flex.py @@ -147,7 +147,7 @@ def deform(obj, definitions, base_names, options): def main(obj, bone_definition, base_names, options): - from Mathutils import Vector, RotationMatrix + from mathutils import Vector, RotationMatrix from math import radians, pi arm = obj.data @@ -177,11 +177,11 @@ def main(obj, bone_definition, base_names, options): # copy the pelvis, offset to make MCH-spine_rotate and MCH-ribcage_hinge ex.ribcage_hinge_e = copy_bone_simple(arm, mt.pelvis, "MCH-%s_hinge" % base_names[mt.ribcage]) ex.ribcage_hinge = ex.ribcage_hinge_e.name - ex.ribcage_hinge_e.translate(Vector(0.0, spine_chain_segment_length / 4.0, 0.0)) + ex.ribcage_hinge_e.translate(Vector((0.0, spine_chain_segment_length / 4.0, 0.0))) ex.spine_rotate_e = copy_bone_simple(arm, mt.ribcage, "MCH-%s_rotate" % spine_chain_basename) ex.spine_rotate = ex.spine_rotate_e.name - ex.spine_rotate_e.translate(Vector(0.0, spine_chain_segment_length / 2.0, 0.0)) + ex.spine_rotate_e.translate(Vector((0.0, spine_chain_segment_length / 2.0, 0.0))) ex.spine_rotate_e.connected = False ex.spine_rotate_e.parent = ex.pelvis_copy_e @@ -294,14 +294,14 @@ def main(obj, bone_definition, base_names, options): con.subtarget = ex.pelvis_copy # add driver - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' var.name = "var" var.targets[0].id_type = 'OBJECT' var.targets[0].id = obj - var.targets[0].data_path = ex.ribcage_copy_p.path_to_id() + '["hinge"]' + var.targets[0].data_path = ex.ribcage_copy_p.path_from_id() + '["hinge"]' mod = fcurve.modifiers[0] mod.poly_order = 1 @@ -347,10 +347,10 @@ def main(obj, bone_definition, base_names, options): # Constrain 'inbetween' bones target_names = [("b%.2d" % (i + 1)) for i in range(spine_chain_len - 1)] - rib_driver_path = ex.ribcage_copy_p.path_to_id() + rib_driver_path = ex.ribcage_copy_p.path_from_id() ex.ribcage_copy_p["bend_tot"] = 0.0 - fcurve = ex.ribcage_copy_p.driver_add('["bend_tot"]', 0) + fcurve = ex.ribcage_copy_p.driver_add('["bend_tot"]') driver = fcurve.driver driver.type = 'SUM' fcurve.modifiers.remove(0) # grr dont need a modifier @@ -385,7 +385,7 @@ def main(obj, bone_definition, base_names, options): del spine_p # add driver - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'SCRIPTED' driver.expression = "bend/bend_tot" @@ -440,7 +440,7 @@ def main(obj, bone_definition, base_names, options): if i == spine_chain_len: con.head_tail = 1.0 - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver var = driver.variables.new() driver.type = 'AVERAGE' @@ -463,7 +463,7 @@ def main(obj, bone_definition, base_names, options): # last step setup layers if "ex_layer" in options: - layer = [n==options["ex_layer"] for n in range(0,32)] + layer = [n == options["ex_layer"] for n in range(0, 32)] else: layer = list(arm.bones[bone_definition[1]].layer) for attr in ex.attr_names: diff --git a/release/scripts/modules/rigify/stretch.py b/release/scripts/modules/rigify/stretch.py index 0902b108def..1c3d317b4b1 100644 --- a/release/scripts/modules/rigify/stretch.py +++ b/release/scripts/modules/rigify/stretch.py @@ -41,19 +41,18 @@ RIG_TYPE = "stretch" # pbone = obj.pose.bones['Bone'] # pbone['type'] = 'copy' -bool_map = {0:False, 1:True, - 0.0:False, 1.0:True, - "false":False, "true":True, - "False":False, "True":True, - "no":False, "yes":True, - "No":False, "Yes":True} +bool_map = {0: False, 1: True, + 0.0: False, 1.0: True, + "false": False, "true": True, + "False": False, "True": True, + "no": False, "yes": True, + "No": False, "Yes": True} + def metarig_definition(obj, orig_bone_name): return (orig_bone_name,) - - def main(obj, bone_definition, base_names, options): """ A stretchy bone from one bone to another. Deformation only (no controls). @@ -108,4 +107,3 @@ def main(obj, bone_definition, base_names, options): con.volume = 'NO_VOLUME' return tuple() - diff --git a/release/scripts/modules/rigify/tail_control.py b/release/scripts/modules/rigify/tail_control.py index 56305b5e07e..47da9778913 100644 --- a/release/scripts/modules/rigify/tail_control.py +++ b/release/scripts/modules/rigify/tail_control.py @@ -22,7 +22,7 @@ import bpy from rigify import RigifyError from rigify_utils import bone_class_instance, copy_bone_simple from rna_prop_ui import rna_idprop_ui_prop_get -from Mathutils import Vector, RotationMatrix +from mathutils import Vector, RotationMatrix from math import radians, pi # not used, defined for completeness @@ -133,9 +133,9 @@ def main(obj, bone_definitions, base_names, options): con_h.subtarget = hinge1 # Add drivers - bone_path = pb[bones[0]].path_to_id() + bone_path = pb[bones[0]].path_from_id() - driver_fcurve = con_f.driver_add("influence", 0) + driver_fcurve = con_f.driver_add("influence") driver = driver_fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -148,7 +148,7 @@ def main(obj, bone_definitions, base_names, options): mod.coefficients[0] = 1.0 mod.coefficients[1] = -1.0 - driver_fcurve = con_h.driver_add("influence", 0) + driver_fcurve = con_h.driver_add("influence") driver = driver_fcurve.driver driver.type = 'AVERAGE' var = driver.variables.new() @@ -163,4 +163,3 @@ def main(obj, bone_definitions, base_names, options): return None - diff --git a/release/scripts/modules/rigify/tongue.py b/release/scripts/modules/rigify/tongue.py new file mode 100644 index 00000000000..b6dfe756b71 --- /dev/null +++ b/release/scripts/modules/rigify/tongue.py @@ -0,0 +1,361 @@ +# ##### 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 rigify import RigifyError +from rigify_utils import bone_class_instance, copy_bone_simple +from rna_prop_ui import rna_idprop_ui_prop_get + +# not used, defined for completeness +METARIG_NAMES = ("body", "head") + + +def metarig_template(): + # TODO: + ## generated by rigify.write_meta_rig + #bpy.ops.object.mode_set(mode='EDIT') + #obj = bpy.context.active_object + #arm = obj.data + #bone = arm.edit_bones.new('body') + #bone.head[:] = 0.0000, -0.0276, -0.1328 + #bone.tail[:] = 0.0000, -0.0170, -0.0197 + #bone.roll = 0.0000 + #bone.connected = False + #bone = arm.edit_bones.new('head') + #bone.head[:] = 0.0000, -0.0170, -0.0197 + #bone.tail[:] = 0.0000, 0.0726, 0.1354 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['body'] + #bone = arm.edit_bones.new('neck.01') + #bone.head[:] = 0.0000, -0.0170, -0.0197 + #bone.tail[:] = 0.0000, -0.0099, 0.0146 + #bone.roll = 0.0000 + #bone.connected = False + #bone.parent = arm.edit_bones['head'] + #bone = arm.edit_bones.new('neck.02') + #bone.head[:] = 0.0000, -0.0099, 0.0146 + #bone.tail[:] = 0.0000, -0.0242, 0.0514 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.01'] + #bone = arm.edit_bones.new('neck.03') + #bone.head[:] = 0.0000, -0.0242, 0.0514 + #bone.tail[:] = 0.0000, -0.0417, 0.0868 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.02'] + #bone = arm.edit_bones.new('neck.04') + #bone.head[:] = 0.0000, -0.0417, 0.0868 + #bone.tail[:] = 0.0000, -0.0509, 0.1190 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.03'] + #bone = arm.edit_bones.new('neck.05') + #bone.head[:] = 0.0000, -0.0509, 0.1190 + #bone.tail[:] = 0.0000, -0.0537, 0.1600 + #bone.roll = 0.0000 + #bone.connected = True + #bone.parent = arm.edit_bones['neck.04'] + # + #bpy.ops.object.mode_set(mode='OBJECT') + #pbone = obj.pose.bones['head'] + #pbone['type'] = 'neck_flex' + pass + + +def metarig_definition(obj, orig_bone_name): + ''' + The bone given is the tongue control, its parent is the body, + # its only child the first of a chain with matching basenames. + eg. + body -> tongue_control -> tongue_01 -> tongue_02 -> tongue_03.... etc + ''' + arm = obj.data + tongue = arm.bones[orig_bone_name] + body = tongue.parent + + children = tongue.children + if len(children) != 1: + raise RigifyError("expected the tongue bone '%s' to have only 1 child." % orig_bone_name) + + child = children[0] + bone_definition = [body.name, tongue.name, child.name] + bone_definition.extend([child.name for child in child.children_recursive_basename]) + return bone_definition + + +def deform(obj, definitions, base_names, options): + for org_bone_name in definitions[2:]: + bpy.ops.object.mode_set(mode='EDIT') + + # Create deform bone. + bone = copy_bone_simple(obj.data, org_bone_name, "DEF-%s" % base_names[org_bone_name], parent=True) + + # Store name before leaving edit mode + bone_name = bone.name + + # Leave edit mode + bpy.ops.object.mode_set(mode='OBJECT') + + # Get the pose bone + bone = obj.pose.bones[bone_name] + + # Constrain to the original bone + # XXX. Todo, is this needed if the bone is connected to its parent? + con = bone.constraints.new('COPY_TRANSFORMS') + con.name = "copy_loc" + con.target = obj + con.subtarget = org_bone_name + + +# TODO: rename all of the head/neck references to tongue +def main(obj, bone_definition, base_names, options): + from mathutils import Vector + + arm = obj.data + + # Initialize container classes for convenience + mt = bone_class_instance(obj, ["body", "head"]) # meta + mt.body = bone_definition[0] + mt.head = bone_definition[1] + mt.update() + + neck_chain = bone_definition[2:] + + mt_chain = bone_class_instance(obj, [("neck_%.2d" % (i + 1)) for i in range(len(neck_chain))]) # 99 bones enough eh? + for i, attr in enumerate(mt_chain.attr_names): + setattr(mt_chain, attr, neck_chain[i]) + mt_chain.update() + + neck_chain_basename = base_names[mt_chain.neck_01_e.name].split(".")[0] + neck_chain_segment_length = mt_chain.neck_01_e.length + + ex = bone_class_instance(obj, ["head", "head_hinge", "neck_socket", "head_ctrl"]) # hinge & extras + + # Add the head hinge at the bodys location, becomes the parent of the original head + + # apply everything to this copy of the chain + ex_chain = mt_chain.copy(base_names=base_names) + ex_chain.neck_01_e.parent = mt_chain.neck_01_e.parent + + + # Copy the head bone and offset + ex.head_e = copy_bone_simple(arm, mt.head, "MCH-%s" % base_names[mt.head], parent=True) + ex.head_e.connected = False + ex.head = ex.head_e.name + # offset + head_length = ex.head_e.length + ex.head_e.head.y += head_length / 2.0 + ex.head_e.tail.y += head_length / 2.0 + + # Yes, use the body bone but call it a head hinge + ex.head_hinge_e = copy_bone_simple(arm, mt.body, "MCH-%s_hinge" % base_names[mt.head], parent=False) + ex.head_hinge_e.connected = False + ex.head_hinge = ex.head_hinge_e.name + ex.head_hinge_e.head.y += head_length / 4.0 + ex.head_hinge_e.tail.y += head_length / 4.0 + + # Insert the neck socket, the head copys this loation + ex.neck_socket_e = arm.edit_bones.new("MCH-%s_socked" % neck_chain_basename) + ex.neck_socket = ex.neck_socket_e.name + ex.neck_socket_e.connected = False + ex.neck_socket_e.parent = mt.body_e + ex.neck_socket_e.head = mt.head_e.head + ex.neck_socket_e.tail = mt.head_e.head - Vector((0.0, neck_chain_segment_length / 2.0, 0.0)) + ex.neck_socket_e.roll = 0.0 + + + # copy of the head for controling + ex.head_ctrl_e = copy_bone_simple(arm, mt.head, base_names[mt.head]) + ex.head_ctrl = ex.head_ctrl_e.name + ex.head_ctrl_e.parent = ex.head_hinge_e + + for i, attr in enumerate(ex_chain.attr_names): + neck_e = getattr(ex_chain, attr + "_e") + + # dont store parent names, re-reference as each chain bones parent. + neck_e_parent = arm.edit_bones.new("MCH-rot_%s" % base_names[getattr(mt_chain, attr)]) + neck_e_parent.head = neck_e.head + neck_e_parent.tail = neck_e.head + (mt.head_e.vector.normalize() * neck_chain_segment_length / 2.0) + neck_e_parent.roll = mt.head_e.roll + + orig_parent = neck_e.parent + neck_e.connected = False + neck_e.parent = neck_e_parent + neck_e_parent.connected = False + + if i == 0: + neck_e_parent.parent = mt.body_e + else: + neck_e_parent.parent = orig_parent + + deform(obj, bone_definition, base_names, options) + + bpy.ops.object.mode_set(mode='OBJECT') + + mt.update() + mt_chain.update() + ex_chain.update() + ex.update() + + # Axis locks + ex.head_ctrl_p.lock_location = True, True, True + ex.head_ctrl_p.lock_scale = True, False, True + + # Simple one off constraints, no drivers + con = ex.head_ctrl_p.constraints.new('COPY_LOCATION') + con.target = obj + con.subtarget = ex.neck_socket + + con = ex.head_p.constraints.new('COPY_ROTATION') + con.target = obj + con.subtarget = ex.head_ctrl + + # driven hinge + prop = rna_idprop_ui_prop_get(ex.head_ctrl_p, "hinge", create=True) + ex.head_ctrl_p["hinge"] = 0.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + con = ex.head_hinge_p.constraints.new('COPY_ROTATION') + con.name = "hinge" + con.target = obj + con.subtarget = mt.body + + # add driver + hinge_driver_path = ex.head_ctrl_p.path_to_id() + '["hinge"]' + + fcurve = con.driver_add("influence") + driver = fcurve.driver + var = driver.variables.new() + driver.type = 'AVERAGE' + var.name = "var" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = hinge_driver_path + + #mod = fcurve_driver.modifiers.new('GENERATOR') + mod = fcurve.modifiers[0] + mod.poly_order = 1 + mod.coefficients[0] = 1.0 + mod.coefficients[1] = -1.0 + + head_driver_path = ex.head_ctrl_p.path_to_id() + + target_names = [("b%.2d" % (i + 1)) for i in range(len(neck_chain))] + + ex.head_ctrl_p["bend_tot"] = 0.0 + fcurve = ex.head_ctrl_p.driver_add('["bend_tot"]') + driver = fcurve.driver + driver.type = 'SUM' + fcurve.modifiers.remove(0) # grr dont need a modifier + + for i in range(len(neck_chain)): + var = driver.variables.new() + var.name = target_names[i] + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = head_driver_path + ('["bend_%.2d"]' % (i + 1)) + + + for i, attr in enumerate(ex_chain.attr_names): + neck_p = getattr(ex_chain, attr + "_p") + neck_p.lock_location = True, True, True + neck_p.lock_location = True, True, True + neck_p.lock_rotations_4d = True + + # Add bend prop + prop_name = "bend_%.2d" % (i + 1) + prop = rna_idprop_ui_prop_get(ex.head_ctrl_p, prop_name, create=True) + ex.head_ctrl_p[prop_name] = 1.0 + prop["soft_min"] = 0.0 + prop["soft_max"] = 1.0 + + # add parent constraint + neck_p_parent = neck_p.parent + + # add constraints + if i == 0: + con = neck_p.constraints.new('COPY_SCALE') + con.name = "Copy Scale" + con.target = obj + con.subtarget = ex.head_ctrl + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + con = neck_p_parent.constraints.new('COPY_ROTATION') + con.name = "Copy Rotation" + con.target = obj + con.subtarget = ex.head + con.owner_space = 'LOCAL' + con.target_space = 'LOCAL' + + fcurve = con.driver_add("influence") + driver = fcurve.driver + driver.type = 'SCRIPTED' + driver.expression = "bend/bend_tot" + + fcurve.modifiers.remove(0) # grr dont need a modifier + + + # add target + var = driver.variables.new() + var.name = "bend_tot" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = head_driver_path + ('["bend_tot"]') + + var = driver.variables.new() + var.name = "bend" + var.targets[0].id_type = 'OBJECT' + var.targets[0].id = obj + var.targets[0].data_path = head_driver_path + ('["%s"]' % prop_name) + + + # finally constrain the original bone to this one + orig_neck_p = getattr(mt_chain, attr + "_p") + con = orig_neck_p.constraints.new('COPY_TRANSFORMS') + con.target = obj + con.subtarget = neck_p.name + + + # Set the head control's custom shape to use the last + # org neck bone for its transform + ex.head_ctrl_p.custom_shape_transform = obj.pose.bones[bone_definition[len(bone_definition)-1]] + + + # last step setup layers + if "ex_layer" in options: + layer = [n==options["ex_layer"] for n in range(0,32)] + else: + layer = list(arm.bones[bone_definition[1]].layer) + for attr in ex_chain.attr_names: + getattr(ex_chain, attr + "_b").layer = layer + for attr in ex.attr_names: + getattr(ex, attr + "_b").layer = layer + + layer = list(arm.bones[bone_definition[1]].layer) + ex.head_ctrl_b.layer = layer + + + # no blending the result of this + return None + diff --git a/release/scripts/modules/rigify/track_dual.py b/release/scripts/modules/rigify/track_dual.py index a926be684e6..38c2a86ab32 100644 --- a/release/scripts/modules/rigify/track_dual.py +++ b/release/scripts/modules/rigify/track_dual.py @@ -41,12 +41,12 @@ RIG_TYPE = "track_dual" # pbone = obj.pose.bones['Bone'] # pbone['type'] = 'copy' -bool_map = {0:False, 1:True, - 0.0:False, 1.0:True, - "false":False, "true":True, - "False":False, "True":True, - "no":False, "yes":True, - "No":False, "Yes":True} +bool_map = {0: False, 1: True, + 0.0: False, 1.0: True, + "false": False, "true": True, + "False": False, "True": True, + "no": False, "yes": True, + "No": False, "Yes": True} def metarig_definition(obj, orig_bone_name): diff --git a/release/scripts/modules/rigify/track_reverse.py b/release/scripts/modules/rigify/track_reverse.py index 988f7576294..21d38c28920 100644 --- a/release/scripts/modules/rigify/track_reverse.py +++ b/release/scripts/modules/rigify/track_reverse.py @@ -98,4 +98,3 @@ def main(obj, bone_definition, base_names, options): return tuple() - diff --git a/release/scripts/modules/rigify_utils.py b/release/scripts/modules/rigify_utils.py index 812342b3ebc..25ab6bebf48 100644 --- a/release/scripts/modules/rigify_utils.py +++ b/release/scripts/modules/rigify_utils.py @@ -26,7 +26,7 @@ # that a generic function would need to check for. import bpy -from Mathutils import Vector +from mathutils import Vector from rna_prop_ui import rna_idprop_ui_prop_get DELIMITER = '-._' @@ -136,7 +136,7 @@ def blend_bone_list(obj, apply_bones, from_bones, to_bones, target_bone=None, ta prop["soft_min"] = 0.0 prop["soft_max"] = 1.0 - driver_path = prop_pbone.path_to_id() + ('["%s"]' % target_prop) + driver_path = prop_pbone.path_from_id() + ('["%s"]' % target_prop) def blend_target(driver): var = driver.variables.new() @@ -154,7 +154,7 @@ def blend_bone_list(obj, apply_bones, from_bones, to_bones, target_bone=None, ta con.target = obj con.subtarget = to_bone_name - fcurve = con.driver_add("influence", 0) + fcurve = con.driver_add("influence") driver = fcurve.driver driver.type = 'AVERAGE' fcurve.modifiers.remove(0) # grr dont need a modifier @@ -204,12 +204,12 @@ def add_pole_target_bone(obj, base_bone_name, name, mode='CROSS'): offset.length = distance elif mode == 'ZAVERAGE': # between both bones Z axis - z_axis_a = base_ebone.matrix.copy().rotation_part() * Vector(0.0, 0.0, -1.0) - z_axis_b = parent_ebone.matrix.copy().rotation_part() * Vector(0.0, 0.0, -1.0) + z_axis_a = base_ebone.matrix.copy().rotation_part() * Vector((0.0, 0.0, -1.0)) + z_axis_b = parent_ebone.matrix.copy().rotation_part() * Vector((0.0, 0.0, -1.0)) offset = (z_axis_a + z_axis_b).normalize() * distance else: # preset axis - offset = Vector(0, 0, 0) + offset = Vector((0.0, 0.0, 0.0)) if mode[0] == "+": val = distance else: diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py index 1c1a32becf9..3fd70fedd8c 100644 --- a/release/scripts/modules/rna_info.py +++ b/release/scripts/modules/rna_info.py @@ -25,6 +25,44 @@ import bpy # use to strip python paths script_paths = bpy.utils.script_paths() +_FAKE_STRUCT_SUBCLASS = True + + +def _get_direct_attr(rna_type, attr): + props = getattr(rna_type, attr) + base = rna_type.base + + if not base: + return [prop for prop in props] + else: + props_base = getattr(base, attr).values() + return [prop for prop in props if prop not in props_base] + + +def get_direct_properties(rna_type): + return _get_direct_attr(rna_type, "properties") + + +def get_direct_functions(rna_type): + return _get_direct_attr(rna_type, "functions") + + +def rna_id_ignore(rna_id): + if rna_id == "rna_type": + return True + + if "_OT_" in rna_id: + return True + if "_MT_" in rna_id: + return True + if "_PT_" in rna_id: + return True + if "_HT_" in rna_id: + return True + if "_KSI_" in rna_id: + return True + return False + def range_str(val): if val < -10000000: @@ -67,8 +105,8 @@ class InfoStructRNA: def build(self): rna_type = self.bl_rna parent_id = self.identifier - self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id) for rna_id, rna_prop in rna_type.properties.items() if rna_id != "rna_type"] - self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id) for rna_prop in rna_type.functions.values()] + self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id) for rna_prop in get_direct_properties(rna_type) if rna_prop.identifier != "rna_type"] + self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id) for rna_prop in get_direct_functions(rna_type)] def get_bases(self): bases = [] @@ -116,17 +154,17 @@ class InfoStructRNA: def __repr__(self): - txt = '' + txt = "" txt += self.identifier if self.base: - txt += '(%s)' % self.base.identifier - txt += ': ' + self.description + '\n' + txt += "(%s)" % self.base.identifier + txt += ": " + self.description + "\n" for prop in self.properties: - txt += prop.__repr__() + '\n' + txt += prop.__repr__() + "\n" for func in self.functions: - txt += func.__repr__() + '\n' + txt += func.__repr__() + "\n" return txt @@ -163,40 +201,40 @@ class InfoPropertyRNA: if self.type == "enum": self.enum_items[:] = rna_prop.items.keys() + if self.array_length: self.default = tuple(getattr(rna_prop, "default_array", ())) + else: + self.default = getattr(rna_prop, "default", None) + self.default_str = "" # fallback + + + if self.type == "pointer": + # pointer has no default, just set as None + self.default = None + self.default_str = "None" + elif self.type == "string": + self.default_str = "\"%s\"" % self.default + elif self.type == "enum": + self.default_str = "'%s'" % self.default + elif self.array_length: self.default_str = '' # special case for floats if len(self.default) > 0: - if type(self.default[0]) is float: + if self.type == "float": self.default_str = "(%s)" % ", ".join([float_as_string(f) for f in self.default]) if not self.default_str: self.default_str = str(self.default) else: - self.default = getattr(rna_prop, "default", "") - if type(self.default) is float: + if self.type == "float": self.default_str = float_as_string(self.default) else: self.default_str = str(self.default) self.srna = GetInfoStructRNA(rna_prop.srna) # valid for pointer/collections - def get_default_string(self): - # pointer has no default, just set as None - if self.type == "pointer": - return "None" - elif self.type == "string": - return '"' + self.default_str + '"' - elif self.type == "enum": - if self.default_str: - return "'" + self.default_str + "'" - else: - return "" - - return self.default_str - def get_arg_default(self, force=True): - default = self.get_default_string() + default = self.default_str if default and (force or self.is_required == False): return "%s=%s" % (self.identifier, default) return self.identifier @@ -212,6 +250,13 @@ class InfoPropertyRNA: 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 not (as_arg or as_ret): + # write default property, ignore function args for this + if self.type != "pointer": + if self.default_str: + type_str += ", default %s" % self.default_str + else: if self.type == "collection": if self.collection_type: @@ -223,17 +268,22 @@ class InfoPropertyRNA: type_str += collection_str + (class_fmt % self.fixed_type.identifier) + # setup qualifiers for this value. + type_info = [] if as_ret: pass elif as_arg: if not self.is_required: - type_str += ", (optional)" + type_info.append("optional") else: # readonly is only useful for selfs, not args if self.is_readonly: - type_str += ", (readonly)" + type_info.append("readonly") if self.is_never_none: - type_str += ", (never None)" + type_info.append("never None") + + if type_info: + type_str += (", (%s)" % ", ".join(type_info)) return type_str @@ -338,7 +388,7 @@ def _GetInfoRNA(bl_rna, cls, parent_id=''): key = parent_id, bl_rna.identifier try: return cls.global_lookup[key] - except: + except KeyError: instance = cls.global_lookup[key] = cls(bl_rna) return instance @@ -366,22 +416,7 @@ def BuildRNAInfo(): rna_full_path_dict = {} # store the result of full_rna_struct_path(rna_struct) rna_children_dict = {} # store all rna_structs nested from here rna_references_dict = {} # store a list of rna path strings that reference this type - rna_functions_dict = {} # store all functions directly in this type (not inherited) - rna_words = set() - - def rna_id_ignore(rna_id): - if rna_id == "rna_type": - return True - - if "_OT_" in rna_id: - return True - if "_MT_" in rna_id: - return True - if "_PT_" in rna_id: - return True - if "_HT_" in rna_id: - return True - return False + # rna_functions_dict = {} # store all functions directly in this type (not inherited) def full_rna_struct_path(rna_struct): ''' @@ -427,7 +462,8 @@ def BuildRNAInfo(): rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct) # Store a list of functions, remove inherited later - rna_functions_dict[identifier] = list(rna_struct.functions) + # NOT USED YET + ## rna_functions_dict[identifier] = get_direct_functions(rna_struct) # fill in these later @@ -439,12 +475,6 @@ def BuildRNAInfo(): print("Ignoring", rna_type_name) - # Sucks but we need to copy this so we can check original parent functions - rna_functions_dict__copy = {} - for key, val in rna_functions_dict.items(): - rna_functions_dict__copy[key] = val[:] - - structs.sort() # not needed but speeds up sort below, setting items without an inheritance first # Arrange so classes are always defined in the correct order @@ -479,41 +509,27 @@ def BuildRNAInfo(): # precalc vars to avoid a lot of looping for (rna_base, identifier, rna_struct) in structs: - if rna_base: - rna_base_prop_keys = rna_struct_dict[rna_base].properties.keys() # could cache - rna_base_func_keys = [f.identifier for f in rna_struct_dict[rna_base].functions] - else: - rna_base_prop_keys = [] - rna_base_func_keys = [] - # rna_struct_path = full_rna_struct_path(rna_struct) rna_struct_path = rna_full_path_dict[identifier] - for rna_prop_identifier, rna_prop in rna_struct.properties.items(): + for rna_prop in get_direct_properties(rna_struct): + rna_prop_identifier = rna_prop.identifier - if rna_prop_identifier == 'RNA' or \ - rna_id_ignore(rna_prop_identifier) or \ - rna_prop_identifier in rna_base_prop_keys: + if rna_prop_identifier == 'RNA' or rna_id_ignore(rna_prop_identifier): continue - for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", None)): # Does this property point to me? if rna_prop_ptr: rna_references_dict[rna_prop_ptr.identifier].append("%s.%s" % (rna_struct_path, rna_prop_identifier)) - for rna_func in rna_struct.functions: + for rna_func in get_direct_functions(rna_struct): for rna_prop_identifier, rna_prop in rna_func.parameters.items(): - if rna_prop_identifier == 'RNA' or \ - rna_id_ignore(rna_prop_identifier) or \ - rna_prop_identifier in rna_base_func_keys: + if rna_prop_identifier == 'RNA' or rna_id_ignore(rna_prop_identifier): continue - try: - rna_prop_ptr = rna_prop.fixed_type - except: - rna_prop_ptr = None + rna_prop_ptr = getattr(rna_prop, "fixed_type", None) # Does this property point to me? if rna_prop_ptr: @@ -526,16 +542,6 @@ def BuildRNAInfo(): rna_children_dict[nested.identifier].append(rna_struct) - if rna_base: - rna_funcs = rna_functions_dict[identifier] - if rna_funcs: - # Remove inherited functions if we have any - rna_base_funcs = rna_functions_dict__copy[rna_base] - rna_funcs[:] = [f for f in rna_funcs if f not in rna_base_funcs] - - rna_functions_dict__copy.clear() - del rna_functions_dict__copy - # Sort the refs, just reads nicer for rna_refs in rna_references_dict.values(): rna_refs.sort() @@ -574,6 +580,16 @@ def BuildRNAInfo(): for prop in func.return_values: prop.build() + if 1: + for rna_info in InfoStructRNA.global_lookup.values(): + for prop in rna_info.properties: + # ERROR CHECK + default = prop.default + if type(default) in (float, int): + if default < prop.min or default > prop.max: + print("\t %s.%s, %s not in [%s - %s]" % (rna_info.identifier, prop.identifier, default, prop.min, prop.max)) + + # now for operators op_mods = dir(bpy.ops) @@ -604,3 +620,23 @@ def BuildRNAInfo(): # print(rna_info) return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup + + +if __name__ == "__main__": + import rna_info + struct = rna_info.BuildRNAInfo()[0] + data = "" + for struct_id, v in sorted(struct.items()): + struct_id_str = "".join(sid for sid in struct_id if struct_id) + props = [(prop.identifier, prop) for prop in v.properties] + + for prop_id, prop in sorted(props): + if prop.type == 'boolean': + continue + data += "%s.%s -> %s: %s%s %s\n" % (struct_id_str, prop.identifier, prop.identifier, prop.type, ", (read-only)" if prop.is_readonly else "", prop.description) + + if bpy.app.background: + print(data) + else: + text = bpy.data.texts.new(name="api.py") + text.from_string(data) diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 246fa4bdb7d..3e8662c275f 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -61,7 +61,7 @@ def rna_idprop_ui_prop_clear(item, prop): def draw(layout, context, context_member, use_edit=True): def assign_props(prop, val, key): - prop.path = context_member + prop.data_path = context_member prop.property = key try: @@ -81,7 +81,7 @@ def draw(layout, context, context_member, use_edit=True): if use_edit: row = layout.row() props = row.operator("wm.properties_add", text="Add") - props.path = context_member + props.data_path = context_member del row for key, val in items: @@ -140,7 +140,7 @@ from bpy.props import * rna_path = StringProperty(name="Property Edit", - description="Property path edit", maxlen=1024, default="", options={'HIDDEN'}) + description="Property data_path edit", maxlen=1024, default="", options={'HIDDEN'}) rna_value = StringProperty(name="Property Value", description="Property value edit", maxlen=1024, default="") @@ -153,11 +153,11 @@ rna_max = FloatProperty(name="Max", default=1.0, precision=3) class WM_OT_properties_edit(bpy.types.Operator): - '''Internal use (edit a property path)''' + '''Internal use (edit a property data_path)''' bl_idname = "wm.properties_edit" bl_label = "Edit Property" - path = rna_path + data_path = rna_path property = rna_property value = rna_value min = rna_min @@ -165,7 +165,7 @@ class WM_OT_properties_edit(bpy.types.Operator): description = StringProperty(name="Tip", default="") def execute(self, context): - path = self.properties.path + data_path = self.properties.data_path value = self.properties.value prop = self.properties.property prop_old = self._last_prop[0] @@ -176,7 +176,7 @@ class WM_OT_properties_edit(bpy.types.Operator): value_eval = value # First remove - item = eval("context.%s" % path) + item = eval("context.%s" % data_path) rna_idprop_ui_prop_clear(item, prop_old) exec_str = "del item['%s']" % prop_old @@ -207,7 +207,7 @@ class WM_OT_properties_edit(bpy.types.Operator): self._last_prop = [self.properties.property] - item = eval("context.%s" % self.properties.path) + item = eval("context.%s" % self.properties.data_path) # setup defaults prop_ui = rna_idprop_ui_prop_get(item, self.properties.property, False) # dont create @@ -225,14 +225,14 @@ class WM_OT_properties_edit(bpy.types.Operator): class WM_OT_properties_add(bpy.types.Operator): - '''Internal use (edit a property path)''' + '''Internal use (edit a property data_path)''' bl_idname = "wm.properties_add" bl_label = "Add Property" - path = rna_path + data_path = rna_path def execute(self, context): - item = eval("context.%s" % self.properties.path) + item = eval("context.%s" % self.properties.data_path) def unique_name(names): prop = 'prop' @@ -251,14 +251,14 @@ class WM_OT_properties_add(bpy.types.Operator): class WM_OT_properties_remove(bpy.types.Operator): - '''Internal use (edit a property path)''' + '''Internal use (edit a property data_path)''' bl_idname = "wm.properties_remove" - bl_label = "Add Property" + bl_label = "Remove Property" - path = rna_path + data_path = rna_path property = rna_property def execute(self, context): - item = eval("context.%s" % self.properties.path) + item = eval("context.%s" % self.properties.data_path) del item[self.properties.property] return {'FINISHED'} diff --git a/release/scripts/op/add_armature_human.py b/release/scripts/op/add_armature_human.py index 1944e73de1b..164bbfb100b 100644 --- a/release/scripts/op/add_armature_human.py +++ b/release/scripts/op/add_armature_human.py @@ -18,7 +18,7 @@ # <pep8-80 compliant> import bpy -import Mathutils +import mathutils from math import cos, sin, pi # could this be stored elsewhere? @@ -609,10 +609,10 @@ def register(): bpy.types.register(AddHuman) bpy.types.INFO_MT_armature_add.append(menu_func) + def unregister(): bpy.types.unregister(AddHuman) bpy.types.INFO_MT_armature_add.remove(menu_func) if __name__ == "__main__": register() - diff --git a/release/scripts/op/add_mesh_torus.py b/release/scripts/op/add_mesh_torus.py index e7c7ac86ee6..16bf4e1c92c 100644 --- a/release/scripts/op/add_mesh_torus.py +++ b/release/scripts/op/add_mesh_torus.py @@ -18,13 +18,13 @@ # <pep8 compliant> import bpy -import Mathutils +import mathutils from math import cos, sin, pi def add_torus(major_rad, minor_rad, major_seg, minor_seg): - Vector = Mathutils.Vector - Quaternion = Mathutils.Quaternion + Vector = mathutils.Vector + Quaternion = mathutils.Quaternion PI_2 = pi * 2 z_axis = (0, 0, 1) @@ -39,10 +39,10 @@ def add_torus(major_rad, minor_rad, major_seg, minor_seg): for minor_index in range(minor_seg): angle = 2 * pi * minor_index / minor_seg - vec = Vector(major_rad + (cos(angle) * minor_rad), 0.0, - (sin(angle) * minor_rad)) * quat + vec = Vector((major_rad + (cos(angle) * minor_rad), 0.0, + (sin(angle) * minor_rad))) * quat - verts.extend([vec.x, vec.y, vec.z]) + verts.extend(vec[:]) if minor_index + 1 == minor_seg: i2 = (major_index) * minor_seg @@ -102,6 +102,10 @@ class AddTorus(bpy.types.Operator): description="Total Interior Radius of the torus", default=0.5, min=0.01, max=100.0) + # generic transform props + location = FloatVectorProperty(name="Location") + rotation = FloatVectorProperty(name="Rotation") + def execute(self, context): props = self.properties @@ -120,44 +124,16 @@ class AddTorus(bpy.types.Operator): mesh.add_geometry(int(len(verts_loc) / 3), 0, int(len(faces) / 4)) mesh.verts.foreach_set("co", verts_loc) mesh.faces.foreach_set("verts_raw", faces) - mesh.faces.foreach_set("smooth", [False] * len(mesh.faces)) - - scene = context.scene - - # ugh - for ob in scene.objects: - ob.selected = False - mesh.update() - ob_new = bpy.data.objects.new("Torus", mesh) - scene.objects.link(ob_new) - ob_new.selected = True - - ob_new.location = scene.cursor_location - - obj_act = scene.objects.active - - if obj_act and obj_act.mode == 'EDIT': - bpy.ops.object.mode_set(mode='OBJECT') - - obj_act.selected = True - scene.update() # apply location - #scene.objects.active = ob_new - - bpy.ops.object.join() # join into the active. - bpy.ops.object.mode_set(mode='EDIT') - else: - scene.objects.active = ob_new - if context.user_preferences.edit.enter_edit_mode: - bpy.ops.object.mode_set(mode='EDIT') + import add_object_utils + add_object_utils.add_object_data(context, mesh, operator=self) return {'FINISHED'} -# Add to the menu -menu_func = (lambda self, context: self.layout.operator(AddTorus.bl_idname, - text="Torus", icon='MESH_DONUT')) +def menu_func(self, context): + self.layout.operator(AddTorus.bl_idname, text="Torus", icon='MESH_DONUT') def register(): diff --git a/release/scripts/op/fcurve_euler_filter.py b/release/scripts/op/fcurve_euler_filter.py index 1c9e9a73312..bba4576ef73 100644 --- a/release/scripts/op/fcurve_euler_filter.py +++ b/release/scripts/op/fcurve_euler_filter.py @@ -1,6 +1,6 @@ from math import * import bpy -from Mathutils import * +from mathutils import * def main(context): def cleanupEulCurve(fcv): diff --git a/release/scripts/op/image.py b/release/scripts/op/image.py index ea5177b1503..be583012d7a 100644 --- a/release/scripts/op/image.py +++ b/release/scripts/op/image.py @@ -19,6 +19,67 @@ # <pep8 compliant> import bpy +from bpy.props import StringProperty + + +class EditExternally(bpy.types.Operator): + '''Edit image in an external application''' + bl_idname = "image.external_edit" + bl_label = "Image Edit Externally" + bl_options = {'REGISTER'} + + filepath = StringProperty(name="File Path", description="Path to an image file", maxlen=1024, default="") + + def _editor_guess(self, context): + import platform + system = platform.system() + + image_editor = context.user_preferences.filepaths.image_editor + + # use image editor in the preferences when available. + if not image_editor: + if system == 'Windows': + image_editor = ["start"] # not tested! + elif system == 'Darwin': + image_editor = ["open"] + else: + image_editor = ["gimp"] + else: + if system == 'Darwin': + # blender file selector treats .app as a folder + # and will include a trailing backslash, so we strip it. + image_editor.rstrip('\\') + image_editor = ["open", "-a", image_editor] + else: + image_editor = [image_editor] + + return image_editor + + def execute(self, context): + import subprocess + filepath = self.properties.filepath + image_editor = self._editor_guess(context) + + cmd = [] + cmd.extend(image_editor) + cmd.append(bpy.utils.expandpath(filepath)) + + subprocess.Popen(cmd) + + return {'FINISHED'} + + def invoke(self, context, event): + try: + filepath = context.space_data.image.filepath + except: + self.report({'ERROR'}, "Image not found on disk") + return {'CANCELLED'} + + self.properties.filepath = filepath + self.execute(context) + + return {'FINISHED'} + class SaveDirty(bpy.types.Operator): '''Select object matching a naming pattern''' @@ -30,23 +91,118 @@ class SaveDirty(bpy.types.Operator): unique_paths = set() for image in bpy.data.images: if image.dirty: - path = bpy.utils.expandpath(image.filename) - if "\\" not in path and "/" not in path: - self.report({'WARNING'}, "Invalid path: " + path) - elif path in unique_paths: - self.report({'WARNING'}, "Path used by more then one image: " + path) + filepath = bpy.utils.expandpath(image.filepath) + if "\\" not in filepath and "/" not in filepath: + self.report({'WARNING'}, "Invalid path: " + filepath) + elif filepath in unique_paths: + self.report({'WARNING'}, "Path used by more then one image: " + filepath) else: - unique_paths.add(path) + unique_paths.add(filepath) image.save() return {'FINISHED'} +class ProjectEdit(bpy.types.Operator): + '''Select object matching a naming pattern''' + bl_idname = "image.project_edit" + bl_label = "Project Edit" + bl_options = {'REGISTER'} + + _proj_hack = [""] + + def execute(self, context): + import os + import subprocess + + EXT = "png" # could be made an option but for now ok + + for image in bpy.data.images: + image.tag = True + + bpy.ops.paint.image_from_view() + + image_new = None + for image in bpy.data.images: + if not image.tag: + image_new = image + break + + if not image_new: + self.report({'ERROR'}, "Could not make new image") + return {'CANCELLED'} + + filepath = os.path.basename(bpy.data.filepath) + filepath = os.path.splitext(filepath)[0] + # filepath = bpy.utils.clean_name(filepath) # fixes <memory> rubbish, needs checking + + if filepath.startswith(".") or filepath == "": + # TODO, have a way to check if the file is saved, assume .B25.blend + tmpdir = context.user_preferences.filepaths.temporary_directory + filepath = os.path.join(tmpdir, "project_edit") + else: + filepath = "//" + filepath + + obj = context.object + + if obj: + filepath += "_" + bpy.utils.clean_name(obj.name) + + filepath_final = filepath + "." + EXT + i = 0 + + while os.path.exists(bpy.utils.expandpath(filepath_final)): + filepath_final = filepath + ("%.3d.%s" % (i, EXT)) + i += 1 + + image_new.name = os.path.basename(filepath_final) + ProjectEdit._proj_hack[0] = image_new.name + + image_new.filepath_raw = filepath_final # TODO, filepath raw is crummy + image_new.file_format = 'PNG' + image_new.save() + + bpy.ops.image.external_edit(filepath=filepath_final) + + return {'FINISHED'} + + +class ProjectApply(bpy.types.Operator): + '''Select object matching a naming pattern''' + bl_idname = "image.project_apply" + bl_label = "Project Apply" + bl_options = {'REGISTER'} + + def execute(self, context): + image_name = ProjectEdit._proj_hack[0] # TODO, deal with this nicer + + try: + image = bpy.data.images[image_name] + except KeyError: + self.report({'ERROR'}, "Could not find image '%s'" % image_name) + return {'CANCELLED'} + + image.reload() + bpy.ops.paint.project_image(image=image_name) + + return {'FINISHED'} + +classes = [ + EditExternally, + SaveDirty, + ProjectEdit, + ProjectApply] + + def register(): - bpy.types.register(SaveDirty) + register = bpy.types.register + for cls in classes: + register(cls) def unregister(): - bpy.types.unregister(SaveDirty) + unregister = bpy.types.unregister + for cls in classes: + unregister(cls) if __name__ == "__main__": register() diff --git a/release/scripts/op/mesh.py b/release/scripts/op/mesh.py index 3989e51b6ca..78ca9a18cdc 100644 --- a/release/scripts/op/mesh.py +++ b/release/scripts/op/mesh.py @@ -76,7 +76,7 @@ class MeshMirrorUV(bpy.types.Operator): def execute(self, context): DIR = 1 # TODO, make an option - from Mathutils import Vector + from mathutils import Vector ob = context.active_object is_editmode = (ob.mode == 'EDIT') diff --git a/release/scripts/op/nla.py b/release/scripts/op/nla.py new file mode 100644 index 00000000000..d0c53758ba9 --- /dev/null +++ b/release/scripts/op/nla.py @@ -0,0 +1,190 @@ +# ##### 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 + + +def pose_info(): + from mathutils import Matrix + + info = {} + + obj = bpy.context.object + pose = obj.pose + + pose_items = pose.bones.items() + + for name, pbone in pose_items: + binfo = {} + bone = pbone.bone + + binfo["parent"] = getattr(bone.parent, "name", None) + binfo["bone"] = bone + binfo["pbone"] = pbone + binfo["matrix_local"] = bone.matrix_local.copy() + try: + binfo["matrix_local_inv"] = binfo["matrix_local"].copy().invert() + except: + binfo["matrix_local_inv"] = Matrix() + + binfo["matrix"] = bone.matrix.copy() + binfo["matrix_pose"] = pbone.matrix.copy() + try: + binfo["matrix_pose_inv"] = binfo["matrix_pose"].copy().invert() + except: + binfo["matrix_pose_inv"] = Matrix() + + print(binfo["matrix_pose"]) + info[name] = binfo + + for name, pbone in pose_items: + binfo = info[name] + binfo_parent = binfo.get("parent", None) + if binfo_parent: + binfo_parent = info[binfo_parent] + + matrix = binfo["matrix_pose"] + rest_matrix = binfo["matrix_local"] + + if binfo_parent: + matrix = binfo_parent["matrix_pose_inv"] * matrix + rest_matrix = binfo_parent["matrix_local_inv"] * rest_matrix + + matrix = rest_matrix.copy().invert() * matrix + + binfo["matrix_key"] = matrix.copy() + + return info + + +def bake(frame_start, frame_end, step=1, only_selected=False): + # import nla; reload(nla); nla.bake() + + scene = bpy.context.scene + obj = bpy.context.object + pose = obj.pose + + info_ls = [] + + frame_range = range(frame_start, frame_end + 1, step) + + # could spped this up by applying steps here too... + for f in frame_range: + scene.set_frame(f) + + info = pose_info() + info_ls.append(info) + f += 1 + + action = bpy.data.actions.new("Action") + + bpy.context.object.animation_data.action = action + + pose_items = pose.bones.items() + + for name, pbone in pose_items: + if only_selected and not pbone.selected: + continue + + for f in frame_range: + matrix = info_ls[int((f - frame_start) / step)][name]["matrix_key"] + + #pbone.location = matrix.translation_part() + #pbone.rotation_quaternion = matrix.to_quat() + pbone.matrix_local = [f for v in matrix for f in v] + + pbone.keyframe_insert("location", -1, f, name) + + rotation_mode = pbone.rotation_mode + + if rotation_mode == 'QUATERNION': + pbone.keyframe_insert("rotation_quaternion", -1, f, name) + elif rotation_mode == 'AXIS_ANGLE': + pbone.keyframe_insert("rotation_axis_angle", -1, f, name) + else: # euler, XYZ, ZXY etc + pbone.keyframe_insert("rotation_euler", -1, f, name) + + pbone.keyframe_insert("scale", -1, f, name) + + return action + + +from bpy.props import * + + +class BakeAction(bpy.types.Operator): + '''Bake animation to an Action''' + bl_idname = "nla.bake" + bl_label = "Bake Action" + bl_options = {'REGISTER', 'UNDO'} + + frame_start = IntProperty(name="Start Frame", + description="Start frame for baking", + default=1, min=1, max=300000) + frame_end = IntProperty(name="End Frame", + description="End frame for baking", + default=250, min=1, max=300000) + step = IntProperty(name="Frame Step", + description="Frame Step", + default=1, min=1, max=120) + only_selected = BoolProperty(name="Only Selected", + default=True) + + def execute(self, context): + props = self.properties + + action = bake(props.frame_start, props.frame_end, props.step, props.only_selected) + + # basic cleanup, could move elsewhere + for fcu in action.fcurves: + keyframe_points = fcu.keyframe_points + i = 1 + while i < len(fcu.keyframe_points) - 1: + val_prev = keyframe_points[i - 1].co[1] + val_next = keyframe_points[i + 1].co[1] + val = keyframe_points[i].co[1] + + if abs(val - val_prev) + abs(val - val_next) < 0.0001: + keyframe_points.remove(keyframe_points[i]) + else: + i += 1 + + return {'FINISHED'} + + def invoke(self, context, event): + wm = context.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.types.register(BakeAction) + # bpy.types.INFO_MT_mesh_add.append(menu_func) + + +def unregister(): + bpy.types.unregister(BakeAction) + # bpy.types.INFO_MT_mesh_add.remove(menu_func) + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/object.py b/release/scripts/op/object.py index 5ff25a47b2f..b16d2b0dcf2 100644 --- a/release/scripts/op/object.py +++ b/release/scripts/op/object.py @@ -113,24 +113,42 @@ class SelectHierarchy(bpy.types.Operator): return context.object def execute(self, context): - obj = context.object - if self.properties.direction == 'PARENT': - parent = obj.parent - if not parent: - return {'CANCELLED'} - obj_act = parent - else: - children = obj.children - if len(children) != 1: - return {'CANCELLED'} - obj_act = children[0] + objs = context.selected_objects + obj_act = context.object + + if context.object not in objs: + objs.append(context.object) if not self.properties.extend: - # obj.selected = False + # for obj in objs: + # obj.selected = False bpy.ops.object.select_all(action='DESELECT') - obj_act.selected = True - context.scene.objects.active = obj_act + if self.properties.direction == 'PARENT': + parents = [] + for obj in objs: + parent = obj.parent + + if parent: + parents.append(parent) + + if obj_act == obj: + context.scene.objects.active = parent + + parent.selected = True + + if parents: + return {'CANCELLED'} + + else: + children = [] + for obj in objs: + children += list(obj.children) + for obj_iter in children: + obj_iter.selected = True + + children.sort(key=lambda obj_iter: obj_iter.name) + context.scene.objects.active = children[0] return {'FINISHED'} @@ -235,8 +253,8 @@ class ShapeTransfer(bpy.types.Operator): ob.active_shape_key_index = len(me.shape_keys.keys) - 1 ob.shape_key_lock = True - from Geometry import BarycentricTransform - from Mathutils import Vector + from geometry import BarycentricTransform + from mathutils import Vector if use_clamp and mode == 'OFFSET': use_clamp = False @@ -383,6 +401,7 @@ class ShapeTransfer(bpy.types.Operator): return {'CANCELLED'} return self._main(ob_act, objects, self.properties.mode, self.properties.use_clamp) + class JoinUVs(bpy.types.Operator): '''Copy UV Layout to objects with matching geometry''' bl_idname = "object.join_uvs" @@ -440,6 +459,7 @@ class JoinUVs(bpy.types.Operator): self._main(context) return {'FINISHED'} + class MakeDupliFace(bpy.types.Operator): '''Make linked objects into dupli-faces''' bl_idname = "object.make_dupli_face" @@ -450,12 +470,12 @@ class MakeDupliFace(bpy.types.Operator): return (obj and obj.type == 'MESH') def _main(self, context): - from Mathutils import Vector + from mathutils import Vector from math import sqrt SCALE_FAC = 0.01 offset = 0.5 * SCALE_FAC - base_tri = Vector(-offset, -offset, 0.0), Vector(offset, -offset, 0.0), Vector(offset, offset, 0.0), Vector(-offset, offset, 0.0) + base_tri = Vector((-offset, -offset, 0.0)), Vector((offset, -offset, 0.0)), Vector((offset, offset, 0.0)), Vector((-offset, offset, 0.0)) def matrix_to_quat(matrix): # scale = matrix.median_scale @@ -471,7 +491,7 @@ class MakeDupliFace(bpy.types.Operator): linked.setdefault(data, []).append(obj) for data, objects in linked.items(): - face_verts = [axis for obj in objects for v in matrix_to_quat(obj.matrix) for axis in v] + face_verts = [axis for obj in objects for v in matrix_to_quat(obj.matrix_world) for axis in v] faces = list(range(int(len(face_verts) / 3))) mesh = bpy.data.meshes.new(data.name + "_dupli") @@ -505,6 +525,26 @@ class MakeDupliFace(bpy.types.Operator): return {'FINISHED'} +class IsolateTypeRender(bpy.types.Operator): + '''Select object matching a naming pattern''' + bl_idname = "object.isolate_type_render" + bl_label = "Isolate Render Selection" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + act_type = context.object.type + + for obj in context.visible_objects: + + if obj.selected: + obj.restrict_render = False + else: + if obj.type == act_type: + obj.restrict_render = True + + return {'FINISHED'} + + classes = [ SelectPattern, SelectCamera, @@ -512,6 +552,7 @@ classes = [ SubdivisionSet, ShapeTransfer, JoinUVs, + IsolateTypeRender, MakeDupliFace] @@ -520,6 +561,7 @@ def register(): for cls in classes: register(cls) + def unregister(): unregister = bpy.types.unregister for cls in classes: @@ -527,4 +569,3 @@ def unregister(): if __name__ == "__main__": register() - diff --git a/release/scripts/op/object_align.py b/release/scripts/op/object_align.py index c5fcc3d2a32..6e5eef0fbc2 100644 --- a/release/scripts/op/object_align.py +++ b/release/scripts/op/object_align.py @@ -19,22 +19,22 @@ # <pep8 compliant> import bpy -from Mathutils import Vector +from mathutils import Vector def align_objects(align_x, align_y, align_z, align_mode, relative_to): cursor = bpy.context.scene.cursor_location - Left_Up_Front_SEL = [[],[],[]] - Right_Down_Back_SEL = [[],[],[]] + Left_Up_Front_SEL = [[], [], []] + Right_Down_Back_SEL = [[], [], []] flag_first = True for obj in bpy.context.selected_objects: if obj.type == 'MESH': - bb_world = [obj.matrix * Vector(v[:]) for v in obj.bound_box] + bb_world = [obj.matrix_world * Vector(v[:]) for v in obj.bound_box] Left_Up_Front = bb_world[1] Right_Down_Back = bb_world[7] @@ -43,13 +43,13 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to): if obj == bpy.context.active_object: - center_active_x = ( Left_Up_Front[0] + Right_Down_Back[0] ) / 2 - center_active_y = ( Left_Up_Front[1] + Right_Down_Back[1] ) / 2 - center_active_z = ( Left_Up_Front[2] + Right_Down_Back[2] ) / 2 + center_active_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2 + center_active_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2 + center_active_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2 - size_active_x = ( Right_Down_Back[0] - Left_Up_Front[0] ) / 2 - size_active_y = ( Right_Down_Back[1] - Left_Up_Front[1] ) / 2 - size_active_z = ( Left_Up_Front[2] - Right_Down_Back[2] ) / 2 + size_active_x = (Right_Down_Back[0] - Left_Up_Front[0]) / 2 + size_active_y = (Right_Down_Back[1] - Left_Up_Front[1]) / 2 + size_active_z = (Left_Up_Front[2] - Right_Down_Back[2]) / 2 # Selection Center @@ -85,9 +85,9 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to): if Right_Down_Back[2] < Right_Down_Back_SEL[2]: Right_Down_Back_SEL[2] = Right_Down_Back[2] - center_sel_x = ( Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0] ) / 2 - center_sel_y = ( Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1] ) / 2 - center_sel_z = ( Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2] ) / 2 + center_sel_x = (Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0]) / 2 + center_sel_y = (Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1]) / 2 + center_sel_z = (Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2]) / 2 # Main Loop @@ -95,14 +95,14 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to): if obj.type == 'MESH': loc_world = obj.location - bb_world = [obj.matrix * Vector(v[:]) for v in obj.bound_box] + bb_world = [obj.matrix_world * Vector(v[:]) for v in obj.bound_box] Left_Up_Front = bb_world[1] Right_Down_Back = bb_world[7] - center_x = ( Left_Up_Front[0] + Right_Down_Back[0] ) / 2 - center_y = ( Left_Up_Front[1] + Right_Down_Back[1] ) / 2 - center_z = ( Left_Up_Front[2] + Right_Down_Back[2] ) / 2 + center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2 + center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2 + center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2 positive_x = Right_Down_Back[0] positive_y = Right_Down_Back[1] @@ -240,8 +240,7 @@ class AlignObjects(bpy.types.Operator): align_mode = bpy.props.EnumProperty(items=( ('OPT_1', "Negative Sides", ""), ('OPT_2', "Centers", ""), - ('OPT_3', "Positive Sides", "") - ), + ('OPT_3', "Positive Sides", "")), name="Align Mode:", description="", default='OPT_2') @@ -250,8 +249,7 @@ class AlignObjects(bpy.types.Operator): ('OPT_1', "Scene Origin", ""), ('OPT_2', "3D Cursor", ""), ('OPT_3', "Selection", ""), - ('OPT_4', "Active", "") - ), + ('OPT_4', "Active", "")), name="Relative To:", description="", default='OPT_4') diff --git a/release/scripts/op/object_randomize_transform.py b/release/scripts/op/object_randomize_transform.py index 3d3feb66424..89884b1767f 100644 --- a/release/scripts/op/object_randomize_transform.py +++ b/release/scripts/op/object_randomize_transform.py @@ -25,12 +25,12 @@ def randomize_selected(seed, loc, rot, scale, scale_even, scale_min): import random from random import uniform - from Mathutils import Vector + from mathutils import Vector random.seed(seed) def rand_vec(vec_range): - return Vector([uniform(- val, val) for val in vec_range]) + return Vector([uniform(-val, val) for val in vec_range]) for obj in bpy.context.selected_objects: diff --git a/release/scripts/op/presets.py b/release/scripts/op/presets.py index 1a8e57f7c32..f80c5e69ddd 100644 --- a/release/scripts/op/presets.py +++ b/release/scripts/op/presets.py @@ -27,8 +27,8 @@ class AddPresetBase(bpy.types.Operator): subclasses must define - preset_values - preset_subdir ''' - bl_idname = "render.preset_add" - bl_label = "Add Render Preset" + # bl_idname = "script.preset_base_add" + # bl_label = "Add a Python Preset" name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen=64, default="") @@ -46,14 +46,18 @@ class AddPresetBase(bpy.types.Operator): target_path = bpy.utils.preset_paths(self.preset_subdir)[0] # we need some way to tell the user and system preset path - file_preset = open(os.path.join(target_path, filename), 'w') + filepath = os.path.join(target_path, filename) + if getattr(self, "save_keyconfig", False): + bpy.ops.wm.keyconfig_export(filepath=filepath, kc_name=self.properties.name) + file_preset = open(filepath, 'a') + file_preset.write("wm.active_keyconfig = kc\n\n") + else: + file_preset = open(filepath, 'w') + file_preset.write("import bpy\n") for rna_path in self.preset_values: value = eval(rna_path) - if type(value) == str: - value = "'%s'" % value - - file_preset.write("%s = %s\n" % (rna_path, value)) + file_preset.write("%s = %s\n" % (rna_path, repr(value))) file_preset.close() @@ -68,6 +72,25 @@ class AddPresetBase(bpy.types.Operator): return {'RUNNING_MODAL'} +class ExecutePreset(bpy.types.Operator): + ''' Executes a preset ''' + bl_idname = "script.execute_preset" + bl_label = "Execute a Python Preset" + + filepath = bpy.props.StringProperty(name="Path", description="Path of the Python file to execute", maxlen=512, default="") + preset_name = bpy.props.StringProperty(name="Preset Name", description="Name of the Preset being executed", default="") + menu_idname = bpy.props.StringProperty(name="Menu ID Name", description="ID name of the menu this was called from", default="") + + def execute(self, context): + # change the menu title to the most recently chosen option + preset_class = getattr(bpy.types, self.properties.menu_idname) + preset_class.bl_label = self.properties.preset_name + + # execute the preset using script.python_file_run + bpy.ops.script.python_file_run(filepath=self.properties.filepath) + return {'FINISHED'} + + class AddPresetRender(AddPresetBase): '''Add a Render Preset''' bl_idname = "render.preset_add" @@ -158,11 +181,35 @@ class AddPresetSunSky(AddPresetBase): preset_subdir = "sunsky" +class AddPresetInteraction(AddPresetBase): + '''Add an Application Interaction Preset''' + bl_idname = "wm.interaction_preset_add" + bl_label = "Add Interaction Preset" + name = AddPresetBase.name + save_keyconfig = True + + preset_values = [ + "bpy.context.user_preferences.edit.drag_immediately", + "bpy.context.user_preferences.edit.insertkey_xyz_to_rgb", + "bpy.context.user_preferences.inputs.select_mouse", + "bpy.context.user_preferences.inputs.zoom_style", + "bpy.context.user_preferences.inputs.zoom_axis", + "bpy.context.user_preferences.inputs.view_rotation", + "bpy.context.user_preferences.inputs.invert_zoom_direction", + "bpy.context.user_preferences.inputs.emulate_numpad", + "bpy.context.user_preferences.inputs.emulate_3_button_mouse", + "bpy.context.user_preferences.inputs.continuous_mouse", + ] + + preset_subdir = "interaction" + classes = [ + ExecutePreset, AddPresetRender, AddPresetSSS, AddPresetCloth, - AddPresetSunSky] + AddPresetSunSky, + AddPresetInteraction] def register(): diff --git a/release/scripts/op/screen_play_rendered_anim.py b/release/scripts/op/screen_play_rendered_anim.py index 17087183c7d..2ecd445df1d 100644 --- a/release/scripts/op/screen_play_rendered_anim.py +++ b/release/scripts/op/screen_play_rendered_anim.py @@ -25,36 +25,40 @@ # Originally written by Matt Ebb import bpy -import subprocess import os -import platform def guess_player_path(preset): + import platform + system = platform.system() + if preset == 'BLENDER24': - player_path = 'blender' + player_path = "blender" - if platform.system() == 'Darwin': - test_path = '/Applications/blender 2.49.app/Contents/MacOS/blender' - elif platform.system() == 'Windows': - test_path = '/Program Files/Blender Foundation/Blender/blender.exe' + if system == 'Darwin': + test_path = "/Applications/blender 2.49.app/Contents/MacOS/blender" + elif system == 'Windows': + test_path = "/Program Files/Blender Foundation/Blender/blender.exe" if os.path.exists(test_path): player_path = test_path elif preset == 'DJV': - player_path = 'djv_view' + player_path = "djv_view" - if platform.system() == 'Darwin': + if system == 'Darwin': test_path = '/Applications/djv-0.8.2.app/Contents/Resources/bin/djv_view' if os.path.exists(test_path): player_path = test_path elif preset == 'FRAMECYCLER': - player_path = 'framecycler' + player_path = "framecycler" elif preset == 'RV': - player_path = 'rv' + player_path = "rv" + + elif preset == 'MPLAYER': + player_path = "mplayer" return player_path @@ -62,11 +66,13 @@ def guess_player_path(preset): class PlayRenderedAnim(bpy.types.Operator): '''Plays back rendered frames/movies using an external player.''' - bl_idname = "screen.play_rendered_anim" + bl_idname = "render.play_rendered_anim" bl_label = "Play Rendered Animation" bl_options = {'REGISTER'} def execute(self, context): + import subprocess + scene = context.scene rd = scene.render prefs = context.user_preferences @@ -74,18 +80,32 @@ class PlayRenderedAnim(bpy.types.Operator): preset = prefs.filepaths.animation_player_preset player_path = prefs.filepaths.animation_player file_path = bpy.utils.expandpath(rd.output_path) + is_movie = rd.is_movie_format # try and guess a command line if it doesn't exist if player_path == '': player_path = guess_player_path(preset) - if preset in ('FRAMECYCLER', 'RV'): + if is_movie == False and preset in ('FRAMECYCLER', 'RV', 'MPLAYER'): # replace the number with '#' - file_a, file_b = rd.frame_path(frame=0), rd.frame_path(frame=1) + file_a = rd.frame_path(frame=0) + + # TODO, make an api call for this + frame_tmp = 9 + file_b = rd.frame_path(frame=frame_tmp) + + while len(file_a) == len(file_b): + frame_tmp = (frame_tmp * 10) + 9 + print(frame_tmp) + file_b = rd.frame_path(frame=frame_tmp) + file_b = rd.frame_path(frame=int(frame_tmp / 10)) + file = ''.join([(c if file_b[i] == c else "#") for i, c in enumerate(file_a)]) else: # works for movies and images - file = rd.frame_path(frame=scene.start_frame) + file = rd.frame_path(frame=scene.frame_start) + + file = bpy.utils.expandpath(file) # expand '//' cmd = [player_path] # extra options, fps controls etc. @@ -96,11 +116,20 @@ class PlayRenderedAnim(bpy.types.Operator): opts = [file, "-playback_speed", str(rd.fps)] cmd.extend(opts) elif preset == 'FRAMECYCLER': - opts = [file, "%d-%d" % (scene.start_frame, scene.end_frame)] + opts = [file, "%d-%d" % (scene.frame_start, scene.frame_end)] cmd.extend(opts) elif preset == 'RV': opts = ["-fps", str(rd.fps), "-play", "[ %s ]" % file] cmd.extend(opts) + elif preset == 'MPLAYER': + opts = [] + if is_movie: + opts.append(file) + else: + opts.append("mf://%s" % file.replace("#", "?")) + opts += ["-mf", "fps=%.4f" % (rd.fps / rd.fps_base)] + opts += ["-loop", "0", "-really-quiet", "-fs"] + cmd.extend(opts) else: # 'CUSTOM' cmd.append(file) @@ -123,4 +152,3 @@ def unregister(): if __name__ == "__main__": register() - diff --git a/release/scripts/op/sequencer.py b/release/scripts/op/sequencer.py index 51bb96442e9..13668498aae 100644 --- a/release/scripts/op/sequencer.py +++ b/release/scripts/op/sequencer.py @@ -20,6 +20,9 @@ import bpy +from bpy.props import * + + class SequencerCrossfadeSounds(bpy.types.Operator): '''Do crossfading volume animation of two selected sound strips.''' @@ -48,34 +51,101 @@ class SequencerCrossfadeSounds(bpy.types.Operator): if seq2 == None: self.report({'ERROR'}, "Select 2 sound strips.") return {'CANCELLED'} - if seq1.start_frame_final > seq2.start_frame_final: + if seq1.frame_final_start > seq2.frame_final_start: s = seq1 seq1 = seq2 seq2 = s - if seq1.end_frame_final > seq2.start_frame_final: - tempcfra = context.scene.current_frame - context.scene.current_frame = seq2.start_frame_final + if seq1.frame_final_end > seq2.frame_final_start: + tempcfra = context.scene.frame_current + context.scene.frame_current = seq2.frame_final_start seq1.keyframe_insert('volume') - context.scene.current_frame = seq1.end_frame_final + context.scene.frame_current = seq1.frame_final_end seq1.volume = 0 seq1.keyframe_insert('volume') seq2.keyframe_insert('volume') - context.scene.current_frame = seq2.start_frame_final + context.scene.frame_current = seq2.frame_final_start seq2.volume = 0 seq2.keyframe_insert('volume') - context.scene.current_frame = tempcfra + context.scene.frame_current = tempcfra return {'FINISHED'} else: self.report({'ERROR'}, "The selected strips don't overlap.") return {'CANCELLED'} +class SequencerCutMulticam(bpy.types.Operator): + '''Cut multicam strip and select camera.''' + + bl_idname = "sequencer.cut_multicam" + bl_label = "Cut multicam" + bl_options = {'REGISTER', 'UNDO'} + + camera = IntProperty(name="Camera", + default=1, min=1, max=32, soft_min=1, soft_max=32) + + def poll(self, context): + if context.scene and context.scene.sequence_editor and context.scene.sequence_editor.active_strip: + return context.scene.sequence_editor.active_strip.type == 'MULTICAM' + else: + return False + + def execute(self, context): + camera = self.properties.camera + + s = context.scene.sequence_editor.active_strip + + if s.multicam_source == camera: + return {'FINISHED'} + + if not s.selected: + s.selected = True + + cfra = context.scene.frame_current + bpy.ops.sequencer.cut(frame=cfra, type='SOFT', side='RIGHT') + for s in context.scene.sequence_editor.sequences_all: + if s.selected and s.type == 'MULTICAM' and s.frame_final_start <= cfra and cfra < s.frame_final_end: + context.scene.sequence_editor.active_strip = s + + context.scene.sequence_editor.active_strip.multicam_source = camera + return {'FINISHED'} + + +class SequencerDeinterlaceSelectedMovies(bpy.types.Operator): + '''Deinterlace all selected movie sources.''' + + bl_idname = "sequencer.deinterlace_selected_movies" + bl_label = "Deinterlace Movies" + bl_options = {'REGISTER', 'UNDO'} + + def poll(self, context): + if context.scene and context.scene.sequence_editor: + return True + else: + return False + + def execute(self, context): + for s in context.scene.sequence_editor.sequences_all: + if s.selected and s.type == 'MOVIE': + s.de_interlace = True + + return {'FINISHED'} + + def register(): - bpy.types.register(SequencerCrossfadeSounds) + register = bpy.types.register + + register(SequencerCrossfadeSounds) + register(SequencerCutMulticam) + register(SequencerDeinterlaceSelectedMovies) + def unregister(): - bpy.types.unregister(SequencerCrossfadeSounds) + unregister = bpy.types.unregister + + unregister(SequencerCrossfadeSounds) + unregister(SequencerCutMulticam) + unregister(SequencerDeinterlaceSelectedMovies) + if __name__ == "__main__": register() - diff --git a/release/scripts/op/uv.py b/release/scripts/op/uv.py index f22db9d87a8..51c1695677b 100644 --- a/release/scripts/op/uv.py +++ b/release/scripts/op/uv.py @@ -29,7 +29,7 @@ class ExportUVLayout(bpy.types.Operator): bl_label = "Export UV Layout" bl_options = {'REGISTER', 'UNDO'} - path = StringProperty(name="File Path", description="File path used for exporting the SVG file", maxlen=1024, default="") + filepath = StringProperty(name="File Path", description="File path used for exporting the SVG file", maxlen=1024, default="") 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=( @@ -113,7 +113,7 @@ class ExportUVLayout(bpy.types.Operator): mode = self.properties.mode - file = open(self.properties.path, "w") + file = open(self.properties.filepath, "w") fw = file.write if mode == 'SVG': @@ -123,7 +123,7 @@ class ExportUVLayout(bpy.types.Operator): 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 = "%s, %s, %s (Blender %s)" % (basename(bpy.data.filename), obj.name, mesh.name, bpy.app.version_string) + desc = "%s, %s, %s (Blender %s)" % (basename(bpy.data.filepath), obj.name, mesh.name, bpy.app.version_string) fw('<desc>%s</desc>\n' % escape(desc)) # svg colors @@ -167,18 +167,34 @@ class ExportUVLayout(bpy.types.Operator): 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 self._face_uv_iter(context): 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('stroke\n') + fw('DRAW\n') fw('showpage\n') fw('%%EOF\n') @@ -194,8 +210,9 @@ class ExportUVLayout(bpy.types.Operator): def menu_func(self, context): - default_path = bpy.data.filename.replace(".blend", ".svg") - self.layout.operator(ExportUVLayout.bl_idname).path = default_path + import os + default_path = os.path.splitext(bpy.data.filepath)[0] + ".svg" + self.layout.operator(ExportUVLayout.bl_idname).filepath = default_path def register(): @@ -203,7 +220,7 @@ def register(): bpy.types.IMAGE_MT_uvs.append(menu_func) -def unreguster(): +def unregister(): bpy.types.unregister(ExportUVLayout) bpy.types.IMAGE_MT_uvs.remove(menu_func) diff --git a/release/scripts/op/uvcalc_follow_active.py b/release/scripts/op/uvcalc_follow_active.py index a76bd0d3a98..61c149c8735 100644 --- a/release/scripts/op/uvcalc_follow_active.py +++ b/release/scripts/op/uvcalc_follow_active.py @@ -65,9 +65,9 @@ def extend(obj, operator, EXTEND_MODE): # vertex index is the key, uv is the value - uvs_vhash_source = dict([(vindex, uvs_source[i]) for i, vindex in enumerate(vidx_source)]) + uvs_vhash_source = {vindex: uvs_source[i] for i, vindex in enumerate(vidx_source)} - uvs_vhash_target = dict([(vindex, uvs_target[i]) for i, vindex in enumerate(vidx_target)]) + uvs_vhash_target = {vindex: uvs_target[i] for i, vindex in enumerate(vidx_target)} edge_idxs_source = face_edge_vs(vidx_source) edge_idxs_target = face_edge_vs(vidx_target) @@ -180,7 +180,7 @@ def extend(obj, operator, EXTEND_MODE): #SEAM = me.edges.seam if EXTEND_MODE == 'LENGTH': - edge_loops = me.edge_loops(face_sel, [ed.key for ed in me.edges if ed.seam]) + edge_loops = me.edge_loops_from_faces(face_sel, [ed.key for ed in me.edges if ed.seam]) me_verts = me.verts for loop in edge_loops: looplen = [0.0] @@ -266,10 +266,10 @@ def register(): bpy.types.register(FollowActiveQuads) bpy.types.VIEW3D_MT_uv_map.append(menu_func) + def unregister(): bpy.types.unregister(FollowActiveQuads) bpy.types.VIEW3D_MT_uv_map.remove(menu_func) if __name__ == "__main__": register() - diff --git a/release/scripts/op/uvcalc_smart_project.py b/release/scripts/op/uvcalc_smart_project.py index c88faae2818..13bf6244f4d 100644 --- a/release/scripts/op/uvcalc_smart_project.py +++ b/release/scripts/op/uvcalc_smart_project.py @@ -22,10 +22,9 @@ # <pep8 compliant> -#from Blender import Object, Draw, Window, sys, Mesh, Geometry -from Mathutils import Matrix, Vector, RotationMatrix +from mathutils import Matrix, Vector, RotationMatrix import time -import Geometry +import geometry import bpy from math import cos, radians @@ -200,7 +199,7 @@ def pointInEdges(pt, edges): """ def pointInIsland(pt, island): - vec1 = Vector(); vec2 = Vector(); vec3 = Vector() + vec1, vec2, vec3 = Vector(), Vector(), Vector() for f in island: vec1.x, vec1.y = f.uv[0] vec2.x, vec2.y = f.uv[1] @@ -227,7 +226,7 @@ def islandIntersectUvIsland(source, target, SourceOffset): # Edge intersect test for ed in edgeLoopsSource: for seg in edgeLoopsTarget: - i = Geometry.LineIntersect2D(\ + i = geometry.LineIntersect2D(\ seg[0], seg[1], SourceOffset+ed[0], SourceOffset+ed[1]) if i: return 1 # LINE INTERSECTION @@ -390,7 +389,7 @@ def mergeUvIslands(islandList): w, h = maxx-minx, maxy-miny totFaceArea = 0 - offset= Vector(minx, miny) + offset= Vector((minx, miny)) for f in islandList[islandIdx]: for uv in f.uv: uv -= offset @@ -514,7 +513,7 @@ def mergeUvIslands(islandList): ##testcount+=1 #print 'Testing intersect' - Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector(boxLeft, boxBottom)) + Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector((boxLeft, boxBottom))) #print 'Done', Intersect if Intersect == 1: # Line intersect, dont bother with this any more pass @@ -540,7 +539,7 @@ def mergeUvIslands(islandList): # Move faces into new island and offset targetIsland[0].extend(sourceIsland[0]) - offset= Vector(boxLeft, boxBottom) + offset= Vector((boxLeft, boxBottom)) for f in sourceIsland[0]: for uv in f.uv: @@ -565,7 +564,7 @@ def mergeUvIslands(islandList): targetIsland[7].extend(sourceIsland[7]) - offset= Vector(boxLeft, boxBottom, 0) + offset= Vector((boxLeft, boxBottom, 0.0)) for p in sourceIsland[7]: p+= offset @@ -741,7 +740,7 @@ def packIslands(islandList): #XXX Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes) ) time1 = time.time() - packWidth, packHeight = Geometry.BoxPack2D(packBoxes) + packWidth, packHeight = geometry.BoxPack2D(packBoxes) # print 'Box Packing Time:', time.time() - time1 @@ -781,9 +780,9 @@ def packIslands(islandList): def VectoMat(vec): a3 = vec.__copy__().normalize() - up = Vector(0,0,1) + up = Vector((0.0, 0.0, 1.0)) if abs(a3.dot(up)) == 1.0: - up = Vector(0,1,0) + up = Vector((0.0, 1.0, 0.0)) a1 = a3.cross(up).normalize() a2 = a3.cross(a1) @@ -936,7 +935,7 @@ def main(context, island_margin, projection_limit): # Initialize projectVecs if USER_VIEW_INIT: # Generate Projection - projectVecs = [Vector(Window.GetViewVector()) * ob.matrixWorld.copy().invert().rotation_part()] # We add to this allong the way + projectVecs = [Vector(Window.GetViewVector()) * ob.matrix_world.copy().invert().rotation_part()] # We add to this allong the way else: projectVecs = [] @@ -964,7 +963,7 @@ def main(context, island_margin, projection_limit): newProjectMeshFaces.append(tempMeshFaces.pop(fIdx)) # Add the average of all these faces normals as a projectionVec - averageVec = Vector(0,0,0) + averageVec = Vector((0.0, 0.0, 0.0)) if USER_AREA_WEIGHT: for fprop in newProjectMeshFaces: averageVec += (fprop.no * fprop.area) @@ -1056,7 +1055,7 @@ def main(context, island_margin, projection_limit): for f in faceProjectionGroupList[i]: f_uv = f.uv for j, v in enumerate(f.v): - # XXX - note, between Mathutils in 2.4 and 2.5 the order changed. + # XXX - note, between mathutils in 2.4 and 2.5 the order changed. f_uv[j][:] = (v.co * MatProj)[:2] diff --git a/release/scripts/op/vertexpaint_dirt.py b/release/scripts/op/vertexpaint_dirt.py index f2c2d5bd864..c1521866a56 100644 --- a/release/scripts/op/vertexpaint_dirt.py +++ b/release/scripts/op/vertexpaint_dirt.py @@ -34,7 +34,7 @@ import bpy import math import time -from Mathutils import Vector +from mathutils import Vector from bpy.props import * @@ -58,17 +58,22 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, con[e.verts[0]].append(e.verts[1]) con[e.verts[1]].append(e.verts[0]) - for v in me.verts: + for i, v in enumerate(me.verts): vec = Vector() no = v.normal co = v.co # get the direction of the vectors between the vertex and it's connected vertices - for c in con[v.index]: - vec += Vector(me.verts[c].co - co).normalize() + for c in con[i]: + vec += (me.verts[c].co - co).normalize() # normalize the vector by dividing by the number of connected verts - vec /= len(con[v.index]) + tot_con = len(con[i]) + + if tot_con == 0: + continue + + vec /= tot_con # angle is the acos of the dot product between vert and connected verts normals ang = math.acos(no.dot(vec)) @@ -79,7 +84,7 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, if not dirt_only: ang = min(clamp_clean, ang) - vert_tone[v.index] = ang + vert_tone[i] = ang # blur tones for i in range(blur_iterations): @@ -146,7 +151,7 @@ def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, class VertexPaintDirt(bpy.types.Operator): - bl_idname = "mesh.vertex_paint_dirt" + bl_idname = "paint.vertex_color_dirt" bl_label = "Dirty Vertex Colors" bl_options = {'REGISTER', 'UNDO'} @@ -171,7 +176,7 @@ class VertexPaintDirt(bpy.types.Operator): print('Dirt calculated in %.6f' % (time.time() - t)) - return('FINISHED',) + return {'FINISHED'} def register(): diff --git a/release/scripts/op/wm.py b/release/scripts/op/wm.py index 216cb57537e..ed77f32ff6d 100644 --- a/release/scripts/op/wm.py +++ b/release/scripts/op/wm.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# <pep8-80 compliant> +# <pep8 compliant> import bpy @@ -24,8 +24,7 @@ from bpy.props import * class MESH_OT_delete_edgeloop(bpy.types.Operator): - '''Export a single object as a stanford PLY with normals, - colours and texture coordinates.''' + '''Delete an edge loop by merging the faces on each side to a single face loop''' bl_idname = "mesh.delete_edgeloop" bl_label = "Delete Edge Loop" @@ -47,10 +46,10 @@ rna_relative_prop = BoolProperty(name="Relative", default=False) -def context_path_validate(context, path): +def context_path_validate(context, data_path): import sys try: - value = eval("context.%s" % path) + value = eval("context.%s" % data_path) except AttributeError: if "'NoneType'" in str(sys.exc_info()[1]): # One of the items in the rna path is None, just ignore this @@ -63,13 +62,13 @@ def context_path_validate(context, path): def execute_context_assign(self, context): - if context_path_validate(context, self.properties.path) is Ellipsis: + if context_path_validate(context, self.properties.data_path) is Ellipsis: return {'PASS_THROUGH'} if getattr(self.properties, "relative", False): - exec("context.%s+=self.properties.value" % self.properties.path) + exec("context.%s+=self.properties.value" % self.properties.data_path) else: - exec("context.%s=self.properties.value" % self.properties.path) + exec("context.%s=self.properties.value" % self.properties.data_path) return {'FINISHED'} @@ -80,7 +79,7 @@ class WM_OT_context_set_boolean(bpy.types.Operator): bl_label = "Context Set Boolean" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value = BoolProperty(name="Value", description="Assignment value", default=True) @@ -93,20 +92,56 @@ class WM_OT_context_set_int(bpy.types.Operator): # same as enum bl_label = "Context Set" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value = IntProperty(name="Value", description="Assign value", default=0) relative = rna_relative_prop execute = execute_context_assign +class WM_OT_context_scale_int(bpy.types.Operator): # same as enum + '''Scale an int context value.''' + bl_idname = "wm.context_scale_int" + bl_label = "Context Set" + bl_options = {'UNDO'} + + data_path = rna_path_prop + value = FloatProperty(name="Value", description="Assign value", default=1.0) + always_step = BoolProperty(name="Always Step", + description="Always adjust the value by a minimum of 1 when 'value' is not 1.0.", + default=True) + + def execute(self, context): + if context_path_validate(context, self.properties.data_path) is Ellipsis: + return {'PASS_THROUGH'} + + value = self.properties.value + data_path = self.properties.data_path + + if value == 1.0: # nothing to do + return {'CANCELLED'} + + if getattr(self.properties, "always_step", False): + if value > 1.0: + add = "1" + func = "max" + else: + add = "-1" + func = "min" + exec("context.%s = %s(round(context.%s * value), context.%s + %s)" % (data_path, func, data_path, data_path, add)) + else: + exec("context.%s *= value" % self.properties.data_path) + + return {'FINISHED'} + + class WM_OT_context_set_float(bpy.types.Operator): # same as enum '''Set a context value.''' bl_idname = "wm.context_set_float" bl_label = "Context Set Float" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value = FloatProperty(name="Value", description="Assignment value", default=0.0) relative = rna_relative_prop @@ -120,7 +155,7 @@ class WM_OT_context_set_string(bpy.types.Operator): # same as enum bl_label = "Context Set String" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value = StringProperty(name="Value", description="Assign value", maxlen=1024, default="") @@ -133,7 +168,7 @@ class WM_OT_context_set_enum(bpy.types.Operator): bl_label = "Context Set Enum" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value = StringProperty(name="Value", description="Assignment value (as a string)", maxlen=1024, default="") @@ -147,15 +182,15 @@ class WM_OT_context_set_value(bpy.types.Operator): bl_label = "Context Set Value" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value = StringProperty(name="Value", description="Assignment value (as a string)", maxlen=1024, default="") def execute(self, context): - if context_path_validate(context, self.properties.path) is Ellipsis: + if context_path_validate(context, self.properties.data_path) is Ellipsis: return {'PASS_THROUGH'} - exec("context.%s=%s" % (self.properties.path, self.properties.value)) + exec("context.%s=%s" % (self.properties.data_path, self.properties.value)) return {'FINISHED'} @@ -165,15 +200,15 @@ class WM_OT_context_toggle(bpy.types.Operator): bl_label = "Context Toggle" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop def execute(self, context): - if context_path_validate(context, self.properties.path) is Ellipsis: + if context_path_validate(context, self.properties.data_path) is Ellipsis: return {'PASS_THROUGH'} exec("context.%s=not (context.%s)" % - (self.properties.path, self.properties.path)) + (self.properties.data_path, self.properties.data_path)) return {'FINISHED'} @@ -184,7 +219,7 @@ class WM_OT_context_toggle_enum(bpy.types.Operator): bl_label = "Context Toggle Values" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop value_1 = StringProperty(name="Value", \ description="Toggle enum", maxlen=1024, default="") @@ -193,12 +228,12 @@ class WM_OT_context_toggle_enum(bpy.types.Operator): def execute(self, context): - if context_path_validate(context, self.properties.path) is Ellipsis: + if context_path_validate(context, self.properties.data_path) is Ellipsis: return {'PASS_THROUGH'} exec("context.%s = ['%s', '%s'][context.%s!='%s']" % \ - (self.properties.path, self.properties.value_1,\ - self.properties.value_2, self.properties.path, + (self.properties.data_path, self.properties.value_1,\ + self.properties.value_2, self.properties.data_path, self.properties.value_2)) return {'FINISHED'} @@ -211,12 +246,12 @@ class WM_OT_context_cycle_int(bpy.types.Operator): bl_label = "Context Int Cycle" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop reverse = rna_reverse_prop def execute(self, context): - path = self.properties.path - value = context_path_validate(context, path) + data_path = self.properties.data_path + value = context_path_validate(context, data_path) if value is Ellipsis: return {'PASS_THROUGH'} @@ -225,16 +260,16 @@ class WM_OT_context_cycle_int(bpy.types.Operator): else: value += 1 - exec("context.%s=value" % path) + exec("context.%s=value" % data_path) - if value != eval("context.%s" % path): + if value != eval("context.%s" % data_path): # relies on rna clamping int's out of the range if self.properties.reverse: value = (1 << 32) else: value = - (1 << 32) - exec("context.%s=value" % path) + exec("context.%s=value" % data_path) return {'FINISHED'} @@ -245,19 +280,19 @@ class WM_OT_context_cycle_enum(bpy.types.Operator): bl_label = "Context Enum Cycle" bl_options = {'UNDO'} - path = rna_path_prop + data_path = rna_path_prop reverse = rna_reverse_prop def execute(self, context): - value = context_path_validate(context, self.properties.path) + value = context_path_validate(context, self.properties.data_path) if value is Ellipsis: return {'PASS_THROUGH'} orig_value = value # Have to get rna enum values - rna_struct_str, rna_prop_str = self.properties.path.rsplit('.', 1) + rna_struct_str, rna_prop_str = self.properties.data_path.rsplit('.', 1) i = rna_prop_str.find('[') # just incse we get "context.foo.bar[0]" @@ -287,7 +322,7 @@ class WM_OT_context_cycle_enum(bpy.types.Operator): advance_enum = enums[orig_index + 1] # set the new value - exec("context.%s=advance_enum" % self.properties.path) + exec("context.%s=advance_enum" % self.properties.data_path) return {'FINISHED'} doc_id = StringProperty(name="Doc ID", @@ -302,27 +337,27 @@ class WM_OT_context_modal_mouse(bpy.types.Operator): bl_idname = "wm.context_modal_mouse" bl_label = "Context Modal Mouse" - path_iter = StringProperty(description="The path relative to the context, must point to an iterable.") - path_item = StringProperty(description="The path from each iterable to the value (int or float)") + data_path_iter = StringProperty(description="The data path relative to the context, must point to an iterable.") + data_path_item = StringProperty(description="The data path from each iterable to the value (int or float)") input_scale = FloatProperty(default=0.01, description="Scale the mouse movement by this value before applying the delta") invert = BoolProperty(default=False, description="Invert the mouse input") initial_x = IntProperty(options={'HIDDEN'}) def _values_store(self, context): - path_iter = self.properties.path_iter - path_item = self.properties.path_item + data_path_iter = self.properties.data_path_iter + data_path_item = self.properties.data_path_item self._values = values = {} - for item in getattr(context, path_iter): + for item in getattr(context, data_path_iter): try: - value_orig = eval("item." + path_item) + value_orig = eval("item." + data_path_item) except: continue # check this can be set, maybe this is library data. try: - exec("item.%s = %s" % (path_item, value_orig)) + exec("item.%s = %s" % (data_path_item, value_orig)) except: continue @@ -333,17 +368,17 @@ class WM_OT_context_modal_mouse(bpy.types.Operator): if self.properties.invert: delta = - delta - path_item = self.properties.path_item + data_path_item = self.properties.data_path_item for item, value_orig in self._values.items(): if type(value_orig) == int: - exec("item.%s = int(%d)" % (path_item, round(value_orig + delta))) + exec("item.%s = int(%d)" % (data_path_item, round(value_orig + delta))) else: - exec("item.%s = %f" % (path_item, value_orig + delta)) + exec("item.%s = %f" % (data_path_item, value_orig + delta)) def _values_restore(self): - path_item = self.properties.path_item + data_path_item = self.properties.data_path_item for item, value_orig in self._values.items(): - exec("item.%s = %s" % (path_item, value_orig)) + exec("item.%s = %s" % (data_path_item, value_orig)) self._values.clear() @@ -371,7 +406,9 @@ class WM_OT_context_modal_mouse(bpy.types.Operator): self._values_store(context) if not self._values: - self.report({'WARNING'}, "Nothing to operate on: %s[ ].%s" % (self.properties.path_iter, self.properties.path_item)) + self.report({'WARNING'}, "Nothing to operate on: %s[ ].%s" % + (self.properties.data_path_iter, self.properties.data_path_item)) + return {'CANCELLED'} else: self.properties.initial_x = event.mouse_x @@ -380,6 +417,52 @@ class WM_OT_context_modal_mouse(bpy.types.Operator): return {'RUNNING_MODAL'} +class WM_OT_url_open(bpy.types.Operator): + "Open a website in the Webbrowser" + bl_idname = "wm.url_open" + bl_label = "" + + url = StringProperty(name="URL", description="URL to open") + + def execute(self, context): + import webbrowser + webbrowser.open(self.properties.url) + return {'FINISHED'} + + +class WM_OT_path_open(bpy.types.Operator): + "Open a path in a file browser" + bl_idname = "wm.path_open" + bl_label = "" + + filepath = StringProperty(name="File Path", maxlen=1024) + + def execute(self, context): + import sys + import os + import subprocess + + filepath = bpy.utils.expandpath(self.properties.filepath) + filepath = os.path.normpath(filepath) + + if not os.path.exists(filepath): + self.report({'ERROR'}, "File '%s' not found" % filepath) + return {'CANCELLED'} + + if sys.platform == 'win32': + subprocess.Popen(['start', filepath], shell=True) + elif sys.platform == 'darwin': + subprocess.Popen(['open', filepath]) + else: + try: + subprocess.Popen(['xdg-open', filepath]) + except OSError: + # xdg-open *should* be supported by recent Gnome, KDE, Xfce + pass + + return {'FINISHED'} + + class WM_OT_doc_view(bpy.types.Operator): '''Load online reference docs''' bl_idname = "wm.doc_view" @@ -449,7 +532,8 @@ class WM_OT_doc_edit(bpy.types.Operator): class_name, class_prop = doc_id.split('.') if not doc_new: - return {'RUNNING_MODAL'} + self.report({'ERROR'}, "No input given for '%s'" % doc_id) + return {'CANCELLED'} # check if this is an operator op_name = class_name.upper() + '_OT_' + class_prop @@ -466,10 +550,6 @@ class WM_OT_doc_edit(bpy.types.Operator): print("op - old:'%s' -> new:'%s'" % (doc_orig, doc_new)) upload["title"] = 'OPERATOR %s:%s' % (doc_id, doc_orig) - upload["description"] = doc_new - - self._send_xmlrpc(upload) - else: rna = getattr(bpy.types, class_name).bl_rna doc_orig = rna.properties[class_prop].description @@ -485,20 +565,15 @@ class WM_OT_doc_edit(bpy.types.Operator): return {'FINISHED'} + def draw(self, context): + layout = self.layout + props = self.properties + layout.label(text="Descriptor ID: '%s'" % props.doc_id) + layout.prop(props, "doc_new", text="") + def invoke(self, context, event): wm = context.manager - return wm.invoke_props_popup(self, event) - - -class WM_OT_reload_scripts(bpy.types.Operator): - '''Load online reference docs''' - bl_idname = "wm.reload_scripts" - bl_label = "Reload Scripts" - - def execute(self, context): - MOD = type(bpy) - bpy.utils.load_scripts(True) - return {'FINISHED'} + return wm.invoke_props_dialog(self, width=600) import rna_prop_ui @@ -508,6 +583,7 @@ classes = [ WM_OT_context_set_boolean, WM_OT_context_set_int, + WM_OT_context_scale_int, WM_OT_context_set_float, WM_OT_context_set_string, WM_OT_context_set_enum, @@ -518,11 +594,12 @@ classes = [ WM_OT_context_cycle_int, WM_OT_context_modal_mouse, + WM_OT_url_open, + WM_OT_path_open, + WM_OT_doc_view, WM_OT_doc_edit, - WM_OT_reload_scripts, - # experemental! rna_prop_ui.WM_OT_properties_edit, rna_prop_ui.WM_OT_properties_add, diff --git a/release/scripts/presets/cloth/cotton.py b/release/scripts/presets/cloth/cotton.py index 7fe8b890b38..695050cf195 100644 --- a/release/scripts/presets/cloth/cotton.py +++ b/release/scripts/presets/cloth/cotton.py @@ -1,3 +1,4 @@ +import bpy bpy.context.cloth.settings.quality = 5 bpy.context.cloth.settings.mass = 0.300 bpy.context.cloth.settings.structural_stiffness = 15.000 diff --git a/release/scripts/presets/cloth/denim.py b/release/scripts/presets/cloth/denim.py index 4cad384a014..bd72fa873da 100644 --- a/release/scripts/presets/cloth/denim.py +++ b/release/scripts/presets/cloth/denim.py @@ -1,3 +1,4 @@ +import bpy bpy.context.cloth.settings.quality = 12 bpy.context.cloth.settings.mass = 1 bpy.context.cloth.settings.structural_stiffness = 40 diff --git a/release/scripts/presets/cloth/leather.py b/release/scripts/presets/cloth/leather.py index 11176c7e814..b85f504b0bb 100644 --- a/release/scripts/presets/cloth/leather.py +++ b/release/scripts/presets/cloth/leather.py @@ -1,3 +1,4 @@ +import bpy bpy.context.cloth.settings.quality = 15 bpy.context.cloth.settings.mass = 0.4 bpy.context.cloth.settings.structural_stiffness = 80 diff --git a/release/scripts/presets/cloth/rubber.py b/release/scripts/presets/cloth/rubber.py index 101c11b141a..c2d7625d333 100644 --- a/release/scripts/presets/cloth/rubber.py +++ b/release/scripts/presets/cloth/rubber.py @@ -1,3 +1,4 @@ +import bpy bpy.context.active_object.modifiers['Cloth'].settings.quality = 7 bpy.context.active_object.modifiers['Cloth'].settings.mass = 3 bpy.context.active_object.modifiers['Cloth'].settings.structural_stiffness = 15 diff --git a/release/scripts/presets/cloth/silk.py b/release/scripts/presets/cloth/silk.py index a0dea5f1fd3..c2a98c270b8 100644 --- a/release/scripts/presets/cloth/silk.py +++ b/release/scripts/presets/cloth/silk.py @@ -1,3 +1,4 @@ +import bpy bpy.context.cloth.settings.quality = 5 bpy.context.cloth.settings.mass = 0.150 bpy.context.cloth.settings.structural_stiffness = 5 diff --git a/release/scripts/presets/ffmpeg/DV.py b/release/scripts/presets/ffmpeg/DV.py new file mode 100644 index 00000000000..4cdb2f271b9 --- /dev/null +++ b/release/scripts/presets/ffmpeg/DV.py @@ -0,0 +1,10 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "DV" +bpy.context.scene.render.resolution_x = 720 + +if is_ntsc: + bpy.context.scene.render.resolution_y = 480 +else: + bpy.context.scene.render.resolution_y = 576 diff --git a/release/scripts/presets/ffmpeg/DVD.py b/release/scripts/presets/ffmpeg/DVD.py new file mode 100644 index 00000000000..233bdab4f20 --- /dev/null +++ b/release/scripts/presets/ffmpeg/DVD.py @@ -0,0 +1,19 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "MPEG2" +bpy.context.scene.render.resolution_x = 720 + +if is_ntsc: + bpy.context.scene.render.resolution_y = 480 + bpy.context.scene.render.ffmpeg_gopsize = 18 +else: + bpy.context.scene.render.resolution_y = 576 + bpy.context.scene.render.ffmpeg_gopsize = 15 + +bpy.context.scene.render.ffmpeg_video_bitrate = 6000 +bpy.context.scene.render.ffmpeg_maxrate = 9000 +bpy.context.scene.render.ffmpeg_minrate = 0 +bpy.context.scene.render.ffmpeg_buffersize = 224*8 +bpy.context.scene.render.ffmpeg_packetsize = 2048 +bpy.context.scene.render.ffmpeg_muxrate = 10080000 diff --git a/release/scripts/presets/ffmpeg/SVCD.py b/release/scripts/presets/ffmpeg/SVCD.py new file mode 100644 index 00000000000..52f938623b8 --- /dev/null +++ b/release/scripts/presets/ffmpeg/SVCD.py @@ -0,0 +1,19 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "MPEG2" +bpy.context.scene.render.resolution_x = 480 + +if is_ntsc: + bpy.context.scene.render.resolution_y = 480 + bpy.context.scene.render.ffmpeg_gopsize = 18 +else: + bpy.context.scene.render.resolution_y = 576 + bpy.context.scene.render.ffmpeg_gopsize = 15 + +bpy.context.scene.render.ffmpeg_video_bitrate = 2040 +bpy.context.scene.render.ffmpeg_maxrate = 2516 +bpy.context.scene.render.ffmpeg_minrate = 0 +bpy.context.scene.render.ffmpeg_buffersize = 224*8 +bpy.context.scene.render.ffmpeg_packetsize = 2324 +bpy.context.scene.render.ffmpeg_muxrate = 0 diff --git a/release/scripts/presets/ffmpeg/VCD.py b/release/scripts/presets/ffmpeg/VCD.py new file mode 100644 index 00000000000..876fa2d8ba6 --- /dev/null +++ b/release/scripts/presets/ffmpeg/VCD.py @@ -0,0 +1,19 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "MPEG1" +bpy.context.scene.render.resolution_x = 352 + +if is_ntsc: + bpy.context.scene.render.resolution_y = 240 + bpy.context.scene.render.ffmpeg_gopsize = 18 +else: + bpy.context.scene.render.resolution_y = 288 + bpy.context.scene.render.ffmpeg_gopsize = 15 + +bpy.context.scene.render.ffmpeg_video_bitrate = 1150 +bpy.context.scene.render.ffmpeg_maxrate = 1150 +bpy.context.scene.render.ffmpeg_minrate = 1150 +bpy.context.scene.render.ffmpeg_buffersize = 40*8 +bpy.context.scene.render.ffmpeg_packetsize = 2324 +bpy.context.scene.render.ffmpeg_muxrate = 2352 * 75 * 8 diff --git a/release/scripts/presets/ffmpeg/h264.py b/release/scripts/presets/ffmpeg/h264.py new file mode 100644 index 00000000000..74e6890a5d4 --- /dev/null +++ b/release/scripts/presets/ffmpeg/h264.py @@ -0,0 +1,17 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "H264" +bpy.context.scene.render.ffmpeg_codec = "H264" + +if is_ntsc: + bpy.context.scene.render.ffmpeg_gopsize = 18 +else: + bpy.context.scene.render.ffmpeg_gopsize = 15 + +bpy.context.scene.render.ffmpeg_video_bitrate = 6000 +bpy.context.scene.render.ffmpeg_maxrate = 9000 +bpy.context.scene.render.ffmpeg_minrate = 0 +bpy.context.scene.render.ffmpeg_buffersize = 224*8 +bpy.context.scene.render.ffmpeg_packetsize = 2048 +bpy.context.scene.render.ffmpeg_muxrate = 10080000 diff --git a/release/scripts/presets/ffmpeg/theora.py b/release/scripts/presets/ffmpeg/theora.py new file mode 100644 index 00000000000..6ce9d4ea7ed --- /dev/null +++ b/release/scripts/presets/ffmpeg/theora.py @@ -0,0 +1,17 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "OGG" +bpy.context.scene.render.ffmpeg_codec = "THEORA" + +if is_ntsc: + bpy.context.scene.render.ffmpeg_gopsize = 18 +else: + bpy.context.scene.render.ffmpeg_gopsize = 15 + +bpy.context.scene.render.ffmpeg_video_bitrate = 6000 +bpy.context.scene.render.ffmpeg_maxrate = 9000 +bpy.context.scene.render.ffmpeg_minrate = 0 +bpy.context.scene.render.ffmpeg_buffersize = 224*8 +bpy.context.scene.render.ffmpeg_packetsize = 2048 +bpy.context.scene.render.ffmpeg_muxrate = 10080000 diff --git a/release/scripts/presets/ffmpeg/xvid.py b/release/scripts/presets/ffmpeg/xvid.py new file mode 100644 index 00000000000..0c8e3989451 --- /dev/null +++ b/release/scripts/presets/ffmpeg/xvid.py @@ -0,0 +1,17 @@ +import bpy +is_ntsc = (bpy.context.scene.render.fps != 25) + +bpy.context.scene.render.ffmpeg_format = "AVI" +bpy.context.scene.render.ffmpeg_codec = "XVID" + +if is_ntsc: + bpy.context.scene.render.ffmpeg_gopsize = 18 +else: + bpy.context.scene.render.ffmpeg_gopsize = 15 + +bpy.context.scene.render.ffmpeg_video_bitrate = 6000 +bpy.context.scene.render.ffmpeg_maxrate = 9000 +bpy.context.scene.render.ffmpeg_minrate = 0 +bpy.context.scene.render.ffmpeg_buffersize = 224*8 +bpy.context.scene.render.ffmpeg_packetsize = 2048 +bpy.context.scene.render.ffmpeg_muxrate = 10080000 diff --git a/release/scripts/presets/interaction/blender.py b/release/scripts/presets/interaction/blender.py new file mode 100644 index 00000000000..63006b2008d --- /dev/null +++ b/release/scripts/presets/interaction/blender.py @@ -0,0 +1,16 @@ +# Configuration Blender +import bpy + +wm = bpy.context.manager +wm.active_keyconfig = wm.keyconfigs['Blender'] + +bpy.context.user_preferences.view.auto_depth = False +bpy.context.user_preferences.view.zoom_to_mouse = False +bpy.context.user_preferences.view.rotate_around_selection = False +bpy.context.user_preferences.edit.drag_immediately = False +bpy.context.user_preferences.edit.insertkey_xyz_to_rgb = False +bpy.context.user_preferences.inputs.select_mouse = 'RIGHT' +bpy.context.user_preferences.inputs.zoom_style = 'DOLLY' +bpy.context.user_preferences.inputs.zoom_axis = 'VERTICAL' +bpy.context.user_preferences.inputs.view_rotation = 'TRACKBALL' +bpy.context.user_preferences.inputs.invert_zoom_direction = False diff --git a/release/scripts/presets/interaction/maya.py b/release/scripts/presets/interaction/maya.py new file mode 100644 index 00000000000..31652035dff --- /dev/null +++ b/release/scripts/presets/interaction/maya.py @@ -0,0 +1,386 @@ +# Configuration Maya +import bpy + +wm = bpy.context.manager +kc = wm.add_keyconfig('Maya') + +# Map 3D View +km = kc.add_keymap('3D View', space_type='VIEW_3D', region_type='WINDOW', modal=False) + +kmi = km.items.add('view3d.manipulator', 'LEFTMOUSE', 'PRESS', any=True) +kmi.properties.release_confirm = True +kmi = km.items.add('view3d.cursor3d', 'ACTIONMOUSE', 'PRESS') +kmi = km.items.add('view3d.rotate', 'LEFTMOUSE', 'PRESS', alt=True) +kmi = km.items.add('view3d.move', 'MIDDLEMOUSE', 'PRESS', alt=True) +kmi = km.items.add('view3d.zoom', 'RIGHTMOUSE', 'PRESS', alt=True) +kmi = km.items.add('view3d.view_selected', 'NUMPAD_PERIOD', 'PRESS') +kmi = km.items.add('view3d.view_center_cursor', 'NUMPAD_PERIOD', 'PRESS', ctrl=True) +kmi = km.items.add('view3d.fly', 'F', 'PRESS', shift=True) +kmi = km.items.add('view3d.smoothview', 'TIMER1', 'ANY', any=True) +kmi = km.items.add('view3d.rotate', 'TRACKPADPAN', 'ANY', alt=True) +kmi = km.items.add('view3d.rotate', 'MOUSEROTATE', 'ANY') +kmi = km.items.add('view3d.move', 'TRACKPADPAN', 'ANY') +kmi = km.items.add('view3d.zoom', 'TRACKPADZOOM', 'ANY') +kmi = km.items.add('view3d.zoom', 'NUMPAD_PLUS', 'PRESS') +kmi.properties.delta = 1 +kmi = km.items.add('view3d.zoom', 'NUMPAD_MINUS', 'PRESS') +kmi.properties.delta = -1 +kmi = km.items.add('view3d.zoom', 'EQUAL', 'PRESS', ctrl=True) +kmi.properties.delta = 1 +kmi = km.items.add('view3d.zoom', 'MINUS', 'PRESS', ctrl=True) +kmi.properties.delta = -1 +kmi = km.items.add('view3d.zoom', 'WHEELINMOUSE', 'PRESS') +kmi.properties.delta = 1 +kmi = km.items.add('view3d.zoom', 'WHEELOUTMOUSE', 'PRESS') +kmi.properties.delta = -1 +kmi = km.items.add('view3d.view_all', 'HOME', 'PRESS') +kmi.properties.center = False +kmi = km.items.add('view3d.view_all', 'C', 'PRESS', shift=True) +kmi.properties.center = True +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_0', 'PRESS') +kmi.properties.type = 'CAMERA' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_1', 'PRESS') +kmi.properties.type = 'FRONT' +kmi = km.items.add('view3d.view_orbit', 'NUMPAD_2', 'PRESS') +kmi.properties.type = 'ORBITDOWN' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_3', 'PRESS') +kmi.properties.type = 'RIGHT' +kmi = km.items.add('view3d.view_orbit', 'NUMPAD_4', 'PRESS') +kmi.properties.type = 'ORBITLEFT' +kmi = km.items.add('view3d.view_persportho', 'NUMPAD_5', 'PRESS') +kmi = km.items.add('view3d.view_orbit', 'NUMPAD_6', 'PRESS') +kmi.properties.type = 'ORBITRIGHT' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_7', 'PRESS') +kmi.properties.type = 'TOP' +kmi = km.items.add('view3d.view_orbit', 'NUMPAD_8', 'PRESS') +kmi.properties.type = 'ORBITUP' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', ctrl=True) +kmi.properties.type = 'BACK' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', ctrl=True) +kmi.properties.type = 'LEFT' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', ctrl=True) +kmi.properties.type = 'BOTTOM' +kmi = km.items.add('view3d.view_pan', 'NUMPAD_2', 'PRESS', ctrl=True) +kmi.properties.type = 'PANDOWN' +kmi = km.items.add('view3d.view_pan', 'NUMPAD_4', 'PRESS', ctrl=True) +kmi.properties.type = 'PANLEFT' +kmi = km.items.add('view3d.view_pan', 'NUMPAD_6', 'PRESS', ctrl=True) +kmi.properties.type = 'PANRIGHT' +kmi = km.items.add('view3d.view_pan', 'NUMPAD_8', 'PRESS', ctrl=True) +kmi.properties.type = 'PANUP' +kmi = km.items.add('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', ctrl=True) +kmi.properties.type = 'PANRIGHT' +kmi = km.items.add('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True) +kmi.properties.type = 'PANLEFT' +kmi = km.items.add('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', shift=True) +kmi.properties.type = 'PANUP' +kmi = km.items.add('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', shift=True) +kmi.properties.type = 'PANDOWN' +kmi = km.items.add('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', ctrl=True, alt=True) +kmi.properties.type = 'ORBITLEFT' +kmi = km.items.add('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True, alt=True) +kmi.properties.type = 'ORBITRIGHT' +kmi = km.items.add('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', shift=True, alt=True) +kmi.properties.type = 'ORBITUP' +kmi = km.items.add('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', shift=True, alt=True) +kmi.properties.type = 'ORBITDOWN' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True) +kmi.properties.align_active = True +kmi.properties.type = 'FRONT' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True) +kmi.properties.align_active = True +kmi.properties.type = 'RIGHT' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True) +kmi.properties.align_active = True +kmi.properties.type = 'TOP' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True, ctrl=True) +kmi.properties.align_active = True +kmi.properties.type = 'BACK' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True, ctrl=True) +kmi.properties.align_active = True +kmi.properties.type = 'LEFT' +kmi = km.items.add('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True, ctrl=True) +kmi.properties.align_active = True +kmi.properties.type = 'BOTTOM' +kmi = km.items.add('view3d.localview', 'NUMPAD_SLASH', 'PRESS') +kmi = km.items.add('view3d.layers', 'ACCENT_GRAVE', 'PRESS') +kmi.properties.nr = 0 +kmi = km.items.add('view3d.layers', 'ONE', 'PRESS', any=True) +kmi.properties.nr = 1 +kmi = km.items.add('view3d.layers', 'TWO', 'PRESS', any=True) +kmi.properties.nr = 2 +kmi = km.items.add('view3d.layers', 'THREE', 'PRESS', any=True) +kmi.properties.nr = 3 +kmi = km.items.add('view3d.layers', 'FOUR', 'PRESS', any=True) +kmi.properties.nr = 4 +kmi = km.items.add('view3d.layers', 'FIVE', 'PRESS', any=True) +kmi.properties.nr = 5 +kmi = km.items.add('view3d.layers', 'SIX', 'PRESS', any=True) +kmi.properties.nr = 6 +kmi = km.items.add('view3d.layers', 'SEVEN', 'PRESS', any=True) +kmi.properties.nr = 7 +kmi = km.items.add('view3d.layers', 'EIGHT', 'PRESS', any=True) +kmi.properties.nr = 8 +kmi = km.items.add('view3d.layers', 'NINE', 'PRESS', any=True) +kmi.properties.nr = 9 +kmi = km.items.add('view3d.layers', 'ZERO', 'PRESS', any=True) +kmi.properties.nr = 10 +kmi = km.items.add('wm.context_toggle_enum', 'Z', 'PRESS') +kmi.properties.data_path = 'space_data.viewport_shading' +kmi.properties.value_1 = 'SOLID' +kmi.properties.value_2 = 'WIREFRAME' +kmi = km.items.add('wm.context_toggle_enum', 'Z', 'PRESS', alt=True) +kmi.properties.data_path = 'space_data.viewport_shading' +kmi.properties.value_1 = 'TEXTURED' +kmi.properties.value_2 = 'SOLID' +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS') +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True) +kmi.properties.extend = True +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True) +kmi.properties.center = True +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS', alt=True) +kmi.properties.enumerate = True +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True) +kmi.properties.center = True +kmi.properties.extend = True +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True) +kmi.properties.center = True +kmi.properties.enumerate = True +kmi = km.items.add('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True) +kmi.properties.enumerate = True +kmi.properties.extend = True +kmi = km.items.add('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.add('view3d.select_border', 'EVT_TWEAK_S', 'ANY') +kmi.properties.extend = False +kmi = km.items.add('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', ctrl=True) +kmi = km.items.add('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', shift=True, ctrl=True) +kmi.properties.deselect = True +kmi = km.items.add('view3d.select_circle', 'C', 'PRESS') +kmi = km.items.add('view3d.clip_border', 'B', 'PRESS', alt=True) +kmi = km.items.add('view3d.zoom_border', 'B', 'PRESS', shift=True) +kmi = km.items.add('view3d.render_border', 'B', 'PRESS', shift=True) +kmi = km.items.add('view3d.camera_to_view', 'NUMPAD_0', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('view3d.object_as_camera', 'NUMPAD_0', 'PRESS', ctrl=True) +kmi = km.items.add('wm.call_menu', 'S', 'PRESS', shift=True) +kmi.properties.name = 'VIEW3D_MT_snap' +kmi = km.items.add('wm.context_set_enum', 'COMMA', 'PRESS') +kmi.properties.data_path = 'space_data.pivot_point' +kmi.properties.value = 'BOUNDING_BOX_CENTER' +kmi = km.items.add('wm.context_set_enum', 'COMMA', 'PRESS', ctrl=True) +kmi.properties.data_path = 'space_data.pivot_point' +kmi.properties.value = 'MEDIAN_POINT' +kmi = km.items.add('wm.context_toggle', 'COMMA', 'PRESS', alt=True) +kmi.properties.data_path = 'space_data.pivot_point_align' +kmi = km.items.add('wm.context_toggle', 'Q', 'PRESS') +kmi.properties.data_path = 'space_data.manipulator' +kmi = km.items.add('wm.context_set_enum', 'PERIOD', 'PRESS') +kmi.properties.data_path = 'space_data.pivot_point' +kmi.properties.value = 'CURSOR' +kmi = km.items.add('wm.context_set_enum', 'PERIOD', 'PRESS', ctrl=True) +kmi.properties.data_path = 'space_data.pivot_point' +kmi.properties.value = 'INDIVIDUAL_ORIGINS' +kmi = km.items.add('wm.context_set_enum', 'PERIOD', 'PRESS', alt=True) +kmi.properties.data_path = 'space_data.pivot_point' +kmi.properties.value = 'ACTIVE_ELEMENT' +kmi = km.items.add('transform.translate', 'G', 'PRESS', shift=True) +kmi = km.items.add('transform.translate', 'EVT_TWEAK_S', 'ANY') +kmi = km.items.add('transform.rotate', 'R', 'PRESS', shift=True) +kmi = km.items.add('transform.resize', 'S', 'PRESS', shift=True) +kmi = km.items.add('transform.warp', 'W', 'PRESS', shift=True) +kmi = km.items.add('transform.tosphere', 'S', 'PRESS', shift=True, alt=True) +kmi = km.items.add('transform.shear', 'S', 'PRESS', shift=True, ctrl=True, alt=True) +kmi = km.items.add('transform.select_orientation', 'SPACE', 'PRESS', alt=True) +kmi = km.items.add('transform.create_orientation', 'SPACE', 'PRESS', ctrl=True, alt=True) +kmi.properties.use = True +kmi = km.items.add('transform.mirror', 'M', 'PRESS', ctrl=True) +kmi = km.items.add('wm.context_toggle', 'TAB', 'PRESS', shift=True) +kmi.properties.data_path = 'tool_settings.snap' +kmi = km.items.add('transform.snap_type', 'TAB', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('view3d.enable_manipulator', 'W', 'PRESS') +kmi.properties.translate = True +kmi = km.items.add('view3d.enable_manipulator', 'E', 'PRESS') +kmi.properties.rotate = True +kmi = km.items.add('view3d.enable_manipulator', 'R', 'PRESS') +kmi.properties.scale = True +kmi = km.items.add('view3d.select_border', 'EVT_TWEAK_S', 'ANY', shift=True) +kmi.properties.extend = True + +# Map Object Mode +km = kc.add_keymap('Object Mode', space_type='EMPTY', region_type='WINDOW', modal=False) + +kmi = km.items.add('wm.context_cycle_enum', 'O', 'PRESS', shift=True) +kmi.properties.data_path = 'tool_settings.proportional_editing_falloff' +kmi = km.items.add('wm.context_toggle_enum', 'O', 'PRESS') +kmi.properties.data_path = 'tool_settings.proportional_editing' +kmi.properties.value_1 = 'DISABLED' +kmi.properties.value_2 = 'ENABLED' +kmi = km.items.add('view3d.game_start', 'P', 'PRESS') +kmi = km.items.add('object.select_all', 'A', 'PRESS') +kmi = km.items.add('object.select_inverse', 'I', 'PRESS', ctrl=True) +kmi = km.items.add('object.select_linked', 'L', 'PRESS', shift=True) +kmi = km.items.add('object.select_grouped', 'G', 'PRESS', shift=True) +kmi = km.items.add('object.select_mirror', 'M', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS') +kmi.properties.direction = 'PARENT' +kmi = km.items.add('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS', shift=True) +kmi.properties.direction = 'PARENT' +kmi.properties.extend = True +kmi = km.items.add('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS') +kmi.properties.direction = 'CHILD' +kmi = km.items.add('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS', shift=True) +kmi.properties.direction = 'CHILD' +kmi.properties.extend = True +kmi = km.items.add('object.parent_set', 'P', 'PRESS', ctrl=True) +kmi = km.items.add('object.parent_no_inverse_set', 'P', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('object.parent_clear', 'P', 'PRESS', alt=True) +kmi = km.items.add('object.track_set', 'T', 'PRESS', ctrl=True) +kmi = km.items.add('object.track_clear', 'T', 'PRESS', alt=True) +kmi = km.items.add('object.constraint_add_with_targets', 'C', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('object.constraints_clear', 'C', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('object.location_clear', 'G', 'PRESS', alt=True) +kmi = km.items.add('object.rotation_clear', 'R', 'PRESS', alt=True) +kmi = km.items.add('object.scale_clear', 'S', 'PRESS', alt=True) +kmi = km.items.add('object.origin_clear', 'O', 'PRESS', alt=True) +kmi = km.items.add('object.restrictview_clear', 'H', 'PRESS', alt=True) +kmi = km.items.add('object.restrictview_set', 'H', 'PRESS') +kmi = km.items.add('object.restrictview_set', 'H', 'PRESS', shift=True) +kmi.properties.unselected = True +kmi = km.items.add('object.move_to_layer', 'M', 'PRESS') +kmi = km.items.add('object.delete', 'X', 'PRESS') +kmi = km.items.add('object.delete', 'DEL', 'PRESS') +kmi = km.items.add('wm.call_menu', 'A', 'PRESS', shift=True) +kmi.properties.name = 'INFO_MT_add' +kmi = km.items.add('object.duplicates_make_real', 'A', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('wm.call_menu', 'A', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_object_apply' +kmi = km.items.add('wm.call_menu', 'U', 'PRESS') +kmi.properties.name = 'VIEW3D_MT_make_single_user' +kmi = km.items.add('wm.call_menu', 'L', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_make_links' +kmi = km.items.add('object.duplicate_move', 'D', 'PRESS', shift=True) +kmi = km.items.add('object.duplicate_move_linked', 'D', 'PRESS', alt=True) +kmi = km.items.add('object.join', 'J', 'PRESS', ctrl=True) +kmi = km.items.add('object.convert', 'C', 'PRESS', alt=True) +kmi = km.items.add('object.proxy_make', 'P', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('object.make_local', 'L', 'PRESS') +kmi = km.items.add('anim.keyframe_insert_menu', 'I', 'PRESS') +kmi = km.items.add('anim.keyframe_delete_v3d', 'I', 'PRESS', alt=True) +kmi = km.items.add('anim.keying_set_active_set', 'I', 'PRESS', shift=True, ctrl=True, alt=True) +kmi = km.items.add('group.create', 'G', 'PRESS', ctrl=True) +kmi = km.items.add('group.objects_remove', 'G', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('group.objects_add_active', 'G', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('group.objects_remove_active', 'G', 'PRESS', shift=True, alt=True) +kmi = km.items.add('wm.call_menu', 'W', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_object_specials' +kmi = km.items.add('object.subdivision_set', 'ZERO', 'PRESS', ctrl=True) +kmi.properties.level = 0 +kmi = km.items.add('object.subdivision_set', 'ONE', 'PRESS', ctrl=True) +kmi.properties.level = 1 +kmi = km.items.add('object.subdivision_set', 'TWO', 'PRESS', ctrl=True) +kmi.properties.level = 2 +kmi = km.items.add('object.subdivision_set', 'THREE', 'PRESS', ctrl=True) +kmi.properties.level = 3 +kmi = km.items.add('object.subdivision_set', 'FOUR', 'PRESS', ctrl=True) +kmi.properties.level = 4 +kmi = km.items.add('object.subdivision_set', 'FIVE', 'PRESS', ctrl=True) +kmi.properties.level = 5 +kmi = km.items.add('object.select_all', 'SELECTMOUSE', 'CLICK') +kmi.properties.action = 'DESELECT' + +# Map Mesh +km = kc.add_keymap('Mesh', space_type='EMPTY', region_type='WINDOW', modal=False) + +kmi = km.items.add('mesh.loopcut_slide', 'R', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.loop_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('mesh.loop_select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True) +kmi.properties.extend = True +kmi = km.items.add('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True) +kmi.properties.extend = True +kmi = km.items.add('mesh.select_shortest_path', 'SELECTMOUSE', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.select_all', 'A', 'PRESS') +kmi = km.items.add('mesh.select_more', 'NUMPAD_PLUS', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.select_less', 'NUMPAD_MINUS', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.select_inverse', 'I', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.select_non_manifold', 'M', 'PRESS', shift=True, ctrl=True, alt=True) +kmi = km.items.add('mesh.select_linked', 'L', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.select_linked_pick', 'L', 'PRESS') +kmi = km.items.add('mesh.select_linked_pick', 'L', 'PRESS', shift=True) +kmi.properties.deselect = True +kmi = km.items.add('mesh.faces_select_linked_flat', 'F', 'PRESS', shift=True, ctrl=True, alt=True) +kmi.properties.sharpness = 135.0 +kmi = km.items.add('mesh.select_similar', 'G', 'PRESS', shift=True) +kmi = km.items.add('wm.call_menu', 'TAB', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_edit_mesh_selection_mode' +kmi = km.items.add('mesh.hide', 'H', 'PRESS') +kmi = km.items.add('mesh.hide', 'H', 'PRESS', shift=True) +kmi.properties.unselected = True +kmi = km.items.add('mesh.reveal', 'H', 'PRESS', alt=True) +kmi = km.items.add('mesh.normals_make_consistent', 'N', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.normals_make_consistent', 'N', 'PRESS', shift=True, ctrl=True) +kmi.properties.inside = True +kmi = km.items.add('view3d.edit_mesh_extrude_move_normal', 'E', 'PRESS', ctrl=True) +kmi = km.items.add('view3d.edit_mesh_extrude_individual_move', 'E', 'PRESS', shift=True) +kmi = km.items.add('wm.call_menu', 'E', 'PRESS', alt=True) +kmi.properties.name = 'VIEW3D_MT_edit_mesh_extrude' +kmi = km.items.add('mesh.spin', 'R', 'PRESS', alt=True) +kmi = km.items.add('mesh.fill', 'F', 'PRESS', alt=True) +kmi = km.items.add('mesh.beautify_fill', 'F', 'PRESS', shift=True, alt=True) +kmi = km.items.add('mesh.quads_convert_to_tris', 'T', 'PRESS', ctrl=True) +kmi = km.items.add('mesh.tris_convert_to_quads', 'J', 'PRESS', alt=True) +kmi = km.items.add('mesh.edge_flip', 'F', 'PRESS', shift=True, ctrl=True) +kmi = km.items.add('mesh.rip_move', 'V', 'PRESS') +kmi = km.items.add('mesh.merge', 'M', 'PRESS', alt=True) +kmi = km.items.add('transform.shrink_fatten', 'S', 'PRESS', ctrl=True, alt=True) +kmi = km.items.add('mesh.edge_face_add', 'F', 'PRESS') +kmi = km.items.add('mesh.duplicate_move', 'D', 'PRESS', shift=True) +kmi = km.items.add('wm.call_menu', 'A', 'PRESS', shift=True) +kmi.properties.name = 'INFO_MT_mesh_add' +kmi = km.items.add('mesh.separate', 'P', 'PRESS') +kmi = km.items.add('mesh.split', 'Y', 'PRESS') +kmi = km.items.add('mesh.dupli_extrude_cursor', 'ACTIONMOUSE', 'CLICK', ctrl=True) +kmi = km.items.add('mesh.delete', 'X', 'PRESS') +kmi = km.items.add('mesh.delete', 'DEL', 'PRESS') +kmi = km.items.add('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', key_modifier='K') +kmi = km.items.add('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', shift=True, key_modifier='K') +kmi.properties.type = 'MIDPOINTS' +kmi = km.items.add('object.vertex_parent_set', 'P', 'PRESS', ctrl=True) +kmi = km.items.add('wm.call_menu', 'W', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_edit_mesh_specials' +kmi = km.items.add('wm.call_menu', 'F', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_edit_mesh_faces' +kmi = km.items.add('wm.call_menu', 'E', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_edit_mesh_edges' +kmi = km.items.add('wm.call_menu', 'V', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_edit_mesh_vertices' +kmi = km.items.add('wm.call_menu', 'H', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_hook' +kmi = km.items.add('wm.call_menu', 'U', 'PRESS') +kmi.properties.name = 'VIEW3D_MT_uv_map' +kmi = km.items.add('wm.call_menu', 'G', 'PRESS', ctrl=True) +kmi.properties.name = 'VIEW3D_MT_vertex_group' +kmi = km.items.add('wm.context_cycle_enum', 'O', 'PRESS', shift=True) +kmi.properties.data_path = 'tool_settings.proportional_editing_falloff' +kmi = km.items.add('wm.context_toggle_enum', 'O', 'PRESS') +kmi.properties.data_path = 'tool_settings.proportional_editing' +kmi.properties.value_1 = 'DISABLED' +kmi.properties.value_2 = 'ENABLED' +kmi = km.items.add('wm.context_toggle_enum', 'O', 'PRESS', alt=True) +kmi.properties.data_path = 'tool_settings.proportional_editing' +kmi.properties.value_1 = 'DISABLED' +kmi.properties.value_2 = 'CONNECTED' +kmi = km.items.add('mesh.select_all', 'SELECTMOUSE', 'CLICK') +kmi.properties.action = 'DESELECT' + +wm.active_keyconfig = kc + +bpy.context.user_preferences.edit.drag_immediately = True +bpy.context.user_preferences.edit.insertkey_xyz_to_rgb = False +bpy.context.user_preferences.inputs.select_mouse = 'LEFT' +bpy.context.user_preferences.inputs.zoom_style = 'DOLLY' +bpy.context.user_preferences.inputs.zoom_axis = 'HORIZONTAL' +bpy.context.user_preferences.inputs.view_rotation = 'TURNTABLE' +bpy.context.user_preferences.inputs.invert_zoom_direction = True diff --git a/release/scripts/presets/render/DVCPRO_HD_1080p.py b/release/scripts/presets/render/DVCPRO_HD_1080p.py new file mode 100644 index 00000000000..f97b389795e --- /dev/null +++ b/release/scripts/presets/render/DVCPRO_HD_1080p.py @@ -0,0 +1,8 @@ +import bpy +bpy.context.scene.render.resolution_x = 1280 +bpy.context.scene.render.resolution_y = 1080 +bpy.context.scene.render.resolution_percentage = 100 +bpy.context.scene.render.pixel_aspect_x = 3 +bpy.context.scene.render.pixel_aspect_y = 2 +bpy.context.scene.render.fps = 24 +bpy.context.scene.render.fps_base = 1 diff --git a/release/scripts/presets/render/DVCPRO_HD_720p.py b/release/scripts/presets/render/DVCPRO_HD_720p.py new file mode 100644 index 00000000000..dbe4c9243d8 --- /dev/null +++ b/release/scripts/presets/render/DVCPRO_HD_720p.py @@ -0,0 +1,8 @@ +import bpy +bpy.context.scene.render.resolution_x = 960 +bpy.context.scene.render.resolution_y = 720 +bpy.context.scene.render.resolution_percentage = 100 +bpy.context.scene.render.pixel_aspect_x = 4 +bpy.context.scene.render.pixel_aspect_y = 3 +bpy.context.scene.render.fps = 24 +bpy.context.scene.render.fps_base = 1 diff --git a/release/scripts/presets/render/HDTV_1080p.py b/release/scripts/presets/render/HDTV_1080p.py index 7693dae9347..32b771dc4a5 100644 --- a/release/scripts/presets/render/HDTV_1080p.py +++ b/release/scripts/presets/render/HDTV_1080p.py @@ -1,3 +1,4 @@ +import bpy bpy.context.scene.render.resolution_x = 1920 bpy.context.scene.render.resolution_y = 1080 bpy.context.scene.render.resolution_percentage = 100 diff --git a/release/scripts/presets/render/HDTV_720p.py b/release/scripts/presets/render/HDTV_720p.py index ee16c1e8b33..c5c81eeb790 100644 --- a/release/scripts/presets/render/HDTV_720p.py +++ b/release/scripts/presets/render/HDTV_720p.py @@ -1,3 +1,4 @@ +import bpy bpy.context.scene.render.resolution_x = 1280 bpy.context.scene.render.resolution_y = 720 bpy.context.scene.render.resolution_percentage = 100 diff --git a/release/scripts/presets/render/HDV_1080p.py b/release/scripts/presets/render/HDV_1080p.py new file mode 100644 index 00000000000..7637648e53a --- /dev/null +++ b/release/scripts/presets/render/HDV_1080p.py @@ -0,0 +1,8 @@ +import bpy +bpy.context.scene.render.resolution_x = 1440 +bpy.context.scene.render.resolution_y = 1080 +bpy.context.scene.render.resolution_percentage = 100 +bpy.context.scene.render.pixel_aspect_x = 4 +bpy.context.scene.render.pixel_aspect_y = 3 +bpy.context.scene.render.fps = 24 +bpy.context.scene.render.fps_base = 1 diff --git a/release/scripts/presets/render/TV_NTSC_16_colon_9.py b/release/scripts/presets/render/TV_NTSC_16_colon_9.py new file mode 100644 index 00000000000..12ecaab4800 --- /dev/null +++ b/release/scripts/presets/render/TV_NTSC_16_colon_9.py @@ -0,0 +1,8 @@ +import bpy +bpy.context.scene.render.resolution_x = 720 +bpy.context.scene.render.resolution_y = 480 +bpy.context.scene.render.resolution_percentage = 100 +bpy.context.scene.render.pixel_aspect_x = 40 +bpy.context.scene.render.pixel_aspect_y = 33 +bpy.context.scene.render.fps = 30 +bpy.context.scene.render.fps_base = 1.001 diff --git a/release/scripts/presets/render/TV_NTSC.py b/release/scripts/presets/render/TV_NTSC_4_colon_3.py index cd6322fd21a..471c45b04d7 100644 --- a/release/scripts/presets/render/TV_NTSC.py +++ b/release/scripts/presets/render/TV_NTSC_4_colon_3.py @@ -1,5 +1,6 @@ +import bpy bpy.context.scene.render.resolution_x = 720 -bpy.context.scene.render.resolution_y = 480 +bpy.context.scene.render.resolution_y = 486 bpy.context.scene.render.resolution_percentage = 100 bpy.context.scene.render.pixel_aspect_x = 10 bpy.context.scene.render.pixel_aspect_y = 11 diff --git a/release/scripts/presets/render/TV_PAL_16_colon_9.py b/release/scripts/presets/render/TV_PAL_16_colon_9.py index 516209a877d..2add29e9408 100644 --- a/release/scripts/presets/render/TV_PAL_16_colon_9.py +++ b/release/scripts/presets/render/TV_PAL_16_colon_9.py @@ -1,7 +1,8 @@ +import bpy bpy.context.scene.render.resolution_x = 720 bpy.context.scene.render.resolution_y = 576 bpy.context.scene.render.resolution_percentage = 100 -bpy.context.scene.render.pixel_aspect_x = 64 -bpy.context.scene.render.pixel_aspect_y = 45 +bpy.context.scene.render.pixel_aspect_x = 16 +bpy.context.scene.render.pixel_aspect_y = 11 bpy.context.scene.render.fps = 25 bpy.context.scene.render.fps_base = 1 diff --git a/release/scripts/presets/render/TV_PAL.py b/release/scripts/presets/render/TV_PAL_4_colon_3.py index 4a71ad021fe..92cb794558e 100644 --- a/release/scripts/presets/render/TV_PAL.py +++ b/release/scripts/presets/render/TV_PAL_4_colon_3.py @@ -1,7 +1,8 @@ +import bpy bpy.context.scene.render.resolution_x = 720 bpy.context.scene.render.resolution_y = 576 bpy.context.scene.render.resolution_percentage = 100 -bpy.context.scene.render.pixel_aspect_x = 54 -bpy.context.scene.render.pixel_aspect_y = 51 +bpy.context.scene.render.pixel_aspect_x = 12 +bpy.context.scene.render.pixel_aspect_y = 11 bpy.context.scene.render.fps = 25 bpy.context.scene.render.fps_base = 1 diff --git a/release/scripts/presets/sss/apple.py b/release/scripts/presets/sss/apple.py index 474769cd36f..d505be2b435 100644 --- a/release/scripts/presets/sss/apple.py +++ b/release/scripts/presets/sss/apple.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 11.605, 3.884, 1.754 bpy.context.active_object.active_material.subsurface_scattering.color = 0.430, 0.210, 0.168 diff --git a/release/scripts/presets/sss/chicken.py b/release/scripts/presets/sss/chicken.py index 6253de09376..53e199455c1 100644 --- a/release/scripts/presets/sss/chicken.py +++ b/release/scripts/presets/sss/chicken.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 9.436, 3.348, 1.790 bpy.context.active_object.active_material.subsurface_scattering.color = 0.439, 0.216, 0.141 diff --git a/release/scripts/presets/sss/cream.py b/release/scripts/presets/sss/cream.py index f0a5292b85c..221739a30ab 100644 --- a/release/scripts/presets/sss/cream.py +++ b/release/scripts/presets/sss/cream.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 15.028, 4.664, 2.541 bpy.context.active_object.active_material.subsurface_scattering.color = 0.987, 0.943, 0.827 diff --git a/release/scripts/presets/sss/ketchup.py b/release/scripts/presets/sss/ketchup.py index caece1ea7ca..96d136dc271 100644 --- a/release/scripts/presets/sss/ketchup.py +++ b/release/scripts/presets/sss/ketchup.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 4.762, 0.575, 0.394 bpy.context.active_object.active_material.subsurface_scattering.color = 0.222, 0.008, 0.002 diff --git a/release/scripts/presets/sss/marble.py b/release/scripts/presets/sss/marble.py index ea894f69800..cd68fd7d26f 100644 --- a/release/scripts/presets/sss/marble.py +++ b/release/scripts/presets/sss/marble.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 8.509, 5.566, 3.951 bpy.context.active_object.active_material.subsurface_scattering.color = 0.925, 0.905, 0.884 diff --git a/release/scripts/presets/sss/potato.py b/release/scripts/presets/sss/potato.py index 89407dff427..7a6c25db833 100644 --- a/release/scripts/presets/sss/potato.py +++ b/release/scripts/presets/sss/potato.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 14.266, 7.228, 2.036 bpy.context.active_object.active_material.subsurface_scattering.color = 0.855, 0.740, 0.292 diff --git a/release/scripts/presets/sss/skim_milk.py b/release/scripts/presets/sss/skim_milk.py index 2e5b19d4f53..5be37b820da 100644 --- a/release/scripts/presets/sss/skim_milk.py +++ b/release/scripts/presets/sss/skim_milk.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 18.424, 10.443, 3.502 bpy.context.active_object.active_material.subsurface_scattering.color = 0.889, 0.888, 0.796 diff --git a/release/scripts/presets/sss/skin1.py b/release/scripts/presets/sss/skin1.py index 42fb1fac43a..76dc64f61f9 100644 --- a/release/scripts/presets/sss/skin1.py +++ b/release/scripts/presets/sss/skin1.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 3.673, 1.367, 0.683 bpy.context.active_object.active_material.subsurface_scattering.color = 0.574, 0.313, 0.174 diff --git a/release/scripts/presets/sss/skin2.py b/release/scripts/presets/sss/skin2.py index 52b649ecd8f..6a72a782638 100644 --- a/release/scripts/presets/sss/skin2.py +++ b/release/scripts/presets/sss/skin2.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 4.821, 1.694, 1.090 bpy.context.active_object.active_material.subsurface_scattering.color = 0.749, 0.571, 0.467 diff --git a/release/scripts/presets/sss/whole_milk.py b/release/scripts/presets/sss/whole_milk.py index 4cb6ccf3dcf..59aaa135933 100644 --- a/release/scripts/presets/sss/whole_milk.py +++ b/release/scripts/presets/sss/whole_milk.py @@ -1,2 +1,3 @@ +import bpy bpy.context.active_object.active_material.subsurface_scattering.radius = 10.899, 6.575, 2.508 bpy.context.active_object.active_material.subsurface_scattering.color = 0.947, 0.931, 0.852 diff --git a/release/scripts/presets/sunsky/classic.py b/release/scripts/presets/sunsky/classic.py index 9ee35d68dad..4394dcce242 100644 --- a/release/scripts/presets/sunsky/classic.py +++ b/release/scripts/presets/sunsky/classic.py @@ -1,3 +1,4 @@ +import bpy bpy.context.object.data.sky.atmosphere_turbidity = 4.0 bpy.context.object.data.sky.sky_blend_type = 'ADD' bpy.context.object.data.sky.sky_blend = 1.0 diff --git a/release/scripts/presets/sunsky/desert.py b/release/scripts/presets/sunsky/desert.py index 26ed4527746..f68ea23845b 100644 --- a/release/scripts/presets/sunsky/desert.py +++ b/release/scripts/presets/sunsky/desert.py @@ -1,3 +1,4 @@ +import bpy bpy.context.object.data.sky.atmosphere_turbidity = 6.0 bpy.context.object.data.sky.sky_blend_type = 'ADD' bpy.context.object.data.sky.sky_blend = 1.0 diff --git a/release/scripts/presets/sunsky/mountain.py b/release/scripts/presets/sunsky/mountain.py index e35d3944042..9b81ffd4399 100644 --- a/release/scripts/presets/sunsky/mountain.py +++ b/release/scripts/presets/sunsky/mountain.py @@ -1,3 +1,4 @@ +import bpy bpy.context.object.data.sky.atmosphere_turbidity = 2.00000023842 bpy.context.object.data.sky.sky_blend_type = 'ADD' bpy.context.object.data.sky.sky_blend = 1.0 diff --git a/release/scripts/templates/gamelogic.py b/release/scripts/templates/gamelogic.py index b4eac81b492..b31d5d95987 100644 --- a/release/scripts/templates/gamelogic.py +++ b/release/scripts/templates/gamelogic.py @@ -7,7 +7,7 @@ # import GameKeys # support for Vector(), Matrix() types and advanced functions like ScaleMatrix(...) and RotationMatrix(...) -# import Mathutils +# import mathutils # for functions like getWindowWidth(), getWindowHeight() # import Rasterizer diff --git a/release/scripts/templates/operator.py b/release/scripts/templates/operator.py index 23d75607ba3..e20b92c9b6a 100644 --- a/release/scripts/templates/operator.py +++ b/release/scripts/templates/operator.py @@ -1,7 +1,6 @@ - import bpy -def write_some_data(context, path, use_some_setting): +def write_some_data(context, filepath, use_some_setting): print("running write_some_data...") pass @@ -16,7 +15,7 @@ class ExportSomeData(bpy.types.Operator): # to the class instance from the operator settings before calling. # TODO, add better example props - path = StringProperty(name="File Path", description="File path used for exporting the PLY file", maxlen= 1024, default= "") + filepath = StringProperty(name="File Path", description="File path used for exporting the PLY file", maxlen= 1024, default= "") 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.")), @@ -30,10 +29,10 @@ class ExportSomeData(bpy.types.Operator): def execute(self, context): # # Bug, currently isnt working - #if not self.is_property_set("path"): + #if not self.is_property_set("filepath"): # raise Exception("filename not set") - write_some_data(self.properties.path, context, self.properties.use_setting) + write_some_data(self.properties.filepath, context, self.properties.use_setting) return {'FINISHED'} @@ -63,4 +62,4 @@ menu_func = lambda self, context: self.layout.operator("export.some_data", text= bpy.types.INFO_MT_file_export.append(menu_func) if __name__ == "__main__": - bpy.ops.export.some_data('INVOKE_DEFAULT', path="/tmp/test.ply") + bpy.ops.export.some_data('INVOKE_DEFAULT', filepath="/tmp/test.ply") diff --git a/release/scripts/templates/operator_modal.py b/release/scripts/templates/operator_modal.py index b2839efabe4..e029d91a7fa 100644 --- a/release/scripts/templates/operator_modal.py +++ b/release/scripts/templates/operator_modal.py @@ -1,3 +1,4 @@ +import bpy from bpy.props import * class ModalOperator(bpy.types.Operator): diff --git a/release/scripts/templates/operator_modal_draw.py b/release/scripts/templates/operator_modal_draw.py index 1f0d7a8504f..1f24672346b 100644 --- a/release/scripts/templates/operator_modal_draw.py +++ b/release/scripts/templates/operator_modal_draw.py @@ -1,13 +1,16 @@ +import bpy import bgl import blf def draw_callback_px(self, context): print("mouse points", len(self.mouse_path)) + font_id = 0 # XXX, need to find out how best to get this. + # draw some text - blf.position(15, 30, 0) - blf.size(20, 72) - blf.draw("Hello Word " + str(len(self.mouse_path))) + blf.position(font_id, 15, 30, 0) + blf.size(font_id, 20, 72) + blf.draw(font_id, "Hello Word " + str(len(self.mouse_path))) # 50% alpha, 2 pixel width line bgl.glEnable(bgl.GL_BLEND) @@ -20,7 +23,7 @@ def draw_callback_px(self, context): bgl.glEnd() - # restore opengl defaults + # restore opengl defaults bgl.glLineWidth(1) bgl.glDisable(bgl.GL_BLEND) bgl.glColor4f(0.0, 0.0, 0.0, 1.0) @@ -50,7 +53,7 @@ class ModalDrawOperator(bpy.types.Operator): def invoke(self, context, event): if context.area.type == 'VIEW_3D': context.manager.add_modal_handler(self) - + # Add the region OpenGL drawing callback # draw in view space with 'POST_VIEW' and 'PRE_VIEW' self._handle = context.region.callback_add(draw_callback_px, (self, context), 'POST_PIXEL') @@ -72,4 +75,4 @@ def unregister(): if __name__ == "__main__": - register()
\ No newline at end of file + register() diff --git a/release/scripts/templates/operator_modal_view3d.py b/release/scripts/templates/operator_modal_view3d.py new file mode 100644 index 00000000000..da019c2b653 --- /dev/null +++ b/release/scripts/templates/operator_modal_view3d.py @@ -0,0 +1,56 @@ +import bpy +from mathutils import Vector +from bpy.props import FloatVectorProperty + +class ViewOperator(bpy.types.Operator): + '''Translate the view using mouse events.''' + bl_idname = "view3d.modal_operator" + bl_label = "Simple View Operator" + + offset = FloatVectorProperty(name="Offset", size=3) + + + def execute(self, context): + v3d = context.space_data + rv3d = v3d.region_3d + + rv3d.view_location = self._initial_location + Vector(self.properties.offset) + + def modal(self, context, event): + v3d = context.space_data + rv3d = v3d.region_3d + + if event.type == 'MOUSEMOVE': + self.properties.offset = (self._initial_mouse - Vector((event.mouse_x, event.mouse_y, 0.0))) * 0.02 + self.execute(context) + + elif event.type == 'LEFTMOUSE': + return {'FINISHED'} + + elif event.type in ('RIGHTMOUSE', 'ESC'): + rv3d.view_location = self._initial_location + return {'CANCELLED'} + + return {'RUNNING_MODAL'} + + def invoke(self, context, event): + + if context.space_data.type == 'VIEW_3D': + v3d = context.space_data + rv3d = v3d.region_3d + + context.manager.add_modal_handler(self) + + if rv3d.view_perspective == 'CAMERA': + rv3d.view_perspective = 'PERSP' + + self._initial_mouse = Vector((event.mouse_x, event.mouse_y, 0.0)) + self._initial_location = rv3d.view_location.copy() + + return {'RUNNING_MODAL'} + else: + self.report({'WARNING'}, "Active space must be a View3d") + return {'CANCELLED'} + + +bpy.types.register(ViewOperator) diff --git a/release/scripts/templates/operator_simple.py b/release/scripts/templates/operator_simple.py index c62d3720d96..985a628c2d0 100644 --- a/release/scripts/templates/operator_simple.py +++ b/release/scripts/templates/operator_simple.py @@ -1,3 +1,4 @@ +import bpy def main(context): for ob in context.scene.objects: @@ -15,7 +16,11 @@ class SimpleOperator(bpy.types.Operator): main(context) return {'FINISHED'} -bpy.types.register(SimpleOperator) +def register(): + bpy.types.register(SimpleOperator) + +def unregister(): + bpy.types.unregister(SimpleOperator) if __name__ == "__main__": - bpy.ops.object.simple_operator() + register() diff --git a/release/scripts/templates/operator_uv.py b/release/scripts/templates/operator_uv.py index 36d8333d49d..1003bd6c58b 100644 --- a/release/scripts/templates/operator_uv.py +++ b/release/scripts/templates/operator_uv.py @@ -1,3 +1,4 @@ +import bpy def main(context): obj = context.active_object diff --git a/release/scripts/ui/properties_animviz.py b/release/scripts/ui/properties_animviz.py index 71b49cea2f2..9eb046cc2c8 100644 --- a/release/scripts/ui/properties_animviz.py +++ b/release/scripts/ui/properties_animviz.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check ################################################ # Generic Panels (Independent of DataType) @@ -49,8 +49,8 @@ class MotionPathButtonsPanel(bpy.types.Panel): sub.prop(mps, "before_current", text="Before") sub.prop(mps, "after_current", text="After") elif (mps.type == 'RANGE'): - sub.prop(mps, "start_frame", text="Start") - sub.prop(mps, "end_frame", text="End") + sub.prop(mps, "frame_start", text="Start") + sub.prop(mps, "frame_end", text="End") sub.prop(mps, "frame_step", text="Step") if bones: @@ -61,6 +61,8 @@ class MotionPathButtonsPanel(bpy.types.Panel): col.label(text="Display:") col.prop(mps, "show_frame_numbers", text="Frame Numbers") col.prop(mps, "highlight_keyframes", text="Keyframes") + if bones: + col.prop(mps, "search_all_action_keyframes", text="+ Non-Grouped Keyframes") col.prop(mps, "show_keyframe_numbers", text="Keyframe Numbers") @@ -88,8 +90,8 @@ class OnionSkinButtonsPanel(bpy.types.Panel): sub = col.column(align=True) if arm.ghost_type == 'RANGE': - sub.prop(arm, "ghost_start_frame", text="Start") - sub.prop(arm, "ghost_end_frame", text="End") + sub.prop(arm, "ghost_frame_start", text="Start") + sub.prop(arm, "ghost_frame_end", text="End") sub.prop(arm, "ghost_size", text="Step") elif arm.ghost_type == 'CURRENT_FRAME': sub.prop(arm, "ghost_step", text="Range") @@ -103,6 +105,7 @@ class OnionSkinButtonsPanel(bpy.types.Panel): ################################################ # Specific Panels for DataTypes + class OBJECT_PT_motion_paths(MotionPathButtonsPanel): #bl_label = "Object Motion Paths" bl_context = "object" @@ -129,13 +132,14 @@ class OBJECT_PT_motion_paths(MotionPathButtonsPanel): col = split.column() col.operator("object.paths_clear", text="Clear Paths") + class OBJECT_PT_onion_skinning(OnionSkinButtonsPanel): #bl_label = "Object Onion Skinning" bl_context = "object" def poll(self, context): return (context.object) - + def draw(self, context): layout = self.layout @@ -144,6 +148,7 @@ class OBJECT_PT_onion_skinning(OnionSkinButtonsPanel): self.draw_settings(context, ob.animation_visualisation, wide_ui) + class DATA_PT_motion_paths(MotionPathButtonsPanel): #bl_label = "Bones Motion Paths" bl_context = "data" @@ -171,6 +176,7 @@ class DATA_PT_motion_paths(MotionPathButtonsPanel): col = split.column() col.operator("pose.paths_clear", text="Clear Paths") + class DATA_PT_onion_skinning(OnionSkinButtonsPanel): #bl_label = "Bones Onion Skinning" bl_context = "data" @@ -187,17 +193,18 @@ class DATA_PT_onion_skinning(OnionSkinButtonsPanel): self.draw_settings(context, ob.pose.animation_visualisation, wide_ui, bones=True) -# NOTE: +# NOTE: # The specialised panel types defined here (i.e. OBJECT_PT_*, etc.) # aren't registered here, but are rather imported to (and registered) # in the files defining the contexts where they reside. Otherwise, # these panels appear at the top of the lists by default. # # However, we keep these empty register funcs here just in case -# something will need them again one day, and also to make +# something will need them again one day, and also to make # it easier to maintain these scripts. classes = [] + def register(): register = bpy.types.register for cls in classes: diff --git a/release/scripts/ui/properties_data_armature.py b/release/scripts/ui/properties_data_armature.py index b7864618de6..38903cc1f27 100644 --- a/release/scripts/ui/properties_data_armature.py +++ b/release/scripts/ui/properties_data_armature.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class DataButtonsPanel(bpy.types.Panel): @@ -69,7 +69,10 @@ class DATA_PT_skeleton(DataButtonsPanel): arm = context.armature wide_ui = context.region.width > narrowui - layout.prop(arm, "pose_position", expand=True) + if wide_ui: + layout.prop(arm, "pose_position", expand=True) + else: + layout.prop(arm, "pose_position", text="") split = layout.split() @@ -79,13 +82,17 @@ class DATA_PT_skeleton(DataButtonsPanel): col.label(text="Protected Layers:") col.prop(arm, "layer_protection", text="") - if wide_ui: - col = split.column() col.label(text="Deform:") + + split = layout.split() + + col = split.column() col.prop(arm, "deform_vertexgroups", text="Vertex Groups") col.prop(arm, "deform_envelope", text="Envelopes") + + if wide_ui: + col = split.column() col.prop(arm, "deform_quaternion", text="Quaternion") - col.prop(arm, "deform_bbone_rest", text="B-Bones Rest") class DATA_PT_display(DataButtonsPanel): @@ -114,7 +121,6 @@ class DATA_PT_display(DataButtonsPanel): col = split.column() col.prop(arm, "draw_group_colors", text="Colors") col.prop(arm, "delay_deform", text="Delay Refresh") - col.prop(ob, "x_ray", text="X-Ray (Object)") class DATA_PT_bone_groups(DataButtonsPanel): @@ -154,13 +160,16 @@ class DATA_PT_bone_groups(DataButtonsPanel): col = split.column() col.template_triColorSet(group, "colors") - row = layout.row(align=True) + row = layout.row() row.active = (ob.proxy is None) - row.operator("pose.group_assign", text="Assign") - row.operator("pose.group_unassign", text="Remove") #row.operator("pose.bone_group_remove_from", text="Remove") - #row.operator("object.bone_group_select", text="Select") - #row.operator("object.bone_group_deselect", text="Deselect") + sub = row.row(align=True) + sub.operator("pose.group_assign", text="Assign") + sub.operator("pose.group_unassign", text="Remove") #row.operator("pose.bone_group_remove_from", text="Remove") + + sub = row.row(align=True) + sub.operator("pose.group_select", text="Select") + sub.operator("pose.group_deselect", text="Deselect") # TODO: this panel will soon be depreceated too @@ -186,8 +195,8 @@ class DATA_PT_ghost(DataButtonsPanel): sub = col.column(align=True) if arm.ghost_type == 'RANGE': - sub.prop(arm, "ghost_start_frame", text="Start") - sub.prop(arm, "ghost_end_frame", text="End") + sub.prop(arm, "ghost_frame_start", text="Start") + sub.prop(arm, "ghost_frame_end", text="End") sub.prop(arm, "ghost_size", text="Step") elif arm.ghost_type == 'CURRENT_FRAME': sub.prop(arm, "ghost_step", text="Range") @@ -253,7 +262,7 @@ class DATA_PT_iksolver_itasc(DataButtonsPanel): row.prop(itasc, "dampmax", text="Damp", slider=True) row.prop(itasc, "dampeps", text="Eps", slider=True) -# import generic panels from other files +# import generic panels from other files from properties_animviz import DATA_PT_motion_paths, DATA_PT_onion_skinning classes = [ diff --git a/release/scripts/ui/properties_data_armature_rigify.py b/release/scripts/ui/properties_data_armature_rigify.py index 99df79ed970..43021858f9f 100644 --- a/release/scripts/ui/properties_data_armature_rigify.py +++ b/release/scripts/ui/properties_data_armature_rigify.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class PoseTemplateSettings(bpy.types.IDPropertyGroup): @@ -29,28 +29,6 @@ class PoseTemplateSettings(bpy.types.IDPropertyGroup): class PoseTemplate(bpy.types.IDPropertyGroup): pass -PoseTemplate.StringProperty(attr="name", - name="Name of the slave", - description="", - maxlen=64, - default="") - - -PoseTemplateSettings.CollectionProperty(attr="templates", type=PoseTemplate, name="Templates", description="") -PoseTemplateSettings.IntProperty(attr="active_template_index", - name="Index of the active slave", - description="", - default=-1, - min=-1, - max=65535) - -PoseTemplateSettings.BoolProperty(attr="generate_def_rig", - name="Create Deform Rig", - description="Create a copy of the metarig, constrainted by the generated rig", - default=False) - -bpy.types.Scene.PointerProperty(attr="pose_templates", type=PoseTemplateSettings, name="Pose Templates", description="Pose Template Settings") - def metarig_templates(): import rigify @@ -234,7 +212,7 @@ class Graph(bpy.types.Operator): import bpy reload(graphviz_export) obj = bpy.context.object - path = os.path.splitext(bpy.data.filename)[0] + "-" + bpy.utils.clean_name(obj.name) + path = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.utils.clean_name(obj.name) path_dot = path + ".dot" path_png = path + ".png" saved = graphviz_export.graph_armature(bpy.context.object, path_dot, CONSTRAINTS=False, DRIVERS=False) @@ -254,14 +232,14 @@ class AsScript(bpy.types.Operator): bl_label = "Write Metarig to Script" bl_options = {'REGISTER', 'UNDO'} - path = StringProperty(name="File Path", description="File path used for exporting the Armature file", maxlen=1024, default="") + filepath = StringProperty(name="File Path", description="File path used for exporting the Armature file", maxlen=1024, default="") def execute(self, context): import rigify_utils reload(rigify_utils) obj = context.object code = rigify_utils.write_meta_rig(obj) - path = self.properties.path + path = self.properties.filepath file = open(path, "w") file.write(code) file.close() @@ -271,7 +249,7 @@ class AsScript(bpy.types.Operator): def invoke(self, context, event): import os obj = context.object - self.properties.path = os.path.splitext(bpy.data.filename)[0] + "-" + bpy.utils.clean_name(obj.name) + ".py" + self.properties.filepath = os.path.splitext(bpy.data.filepath)[0] + "-" + bpy.utils.clean_name(obj.name) + ".py" wm = context.manager wm.add_fileselect(self) return {'RUNNING_MODAL'} @@ -356,6 +334,28 @@ def register(): for cls in classes: register(cls) + PoseTemplate.StringProperty(attr="name", + name="Name of the slave", + description="", + maxlen=64, + default="") + + + PoseTemplateSettings.CollectionProperty(attr="templates", type=PoseTemplate, name="Templates", description="") + PoseTemplateSettings.IntProperty(attr="active_template_index", + name="Index of the active slave", + description="", + default=-1, + min=-1, + max=65535) + + PoseTemplateSettings.BoolProperty(attr="generate_def_rig", + name="Create Deform Rig", + description="Create a copy of the metarig, constrainted by the generated rig", + default=False) + + bpy.types.Scene.PointerProperty(attr="pose_templates", type=PoseTemplateSettings, name="Pose Templates", description="Pose Template Settings") + space_info.INFO_MT_armature_add.append(menu_func) diff --git a/release/scripts/ui/properties_data_bone.py b/release/scripts/ui/properties_data_bone.py index a6bac3e6aaa..b6b638ef380 100644 --- a/release/scripts/ui/properties_data_bone.py +++ b/release/scripts/ui/properties_data_bone.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class BoneButtonsPanel(bpy.types.Panel): @@ -257,6 +257,9 @@ class BONE_PT_inverse_kinematics(BoneButtonsPanel): pchan = ob.pose.bones[bone.name] wide_ui = context.region.width > narrowui + row = layout.row() + row.prop(ob.pose, "ik_solver") + split = layout.split(percentage=0.25) split.prop(pchan, "ik_dof_x", text="X") split.active = pchan.has_ik diff --git a/release/scripts/ui/properties_data_camera.py b/release/scripts/ui/properties_data_camera.py index be0a4f84145..3abd460c427 100644 --- a/release/scripts/ui/properties_data_camera.py +++ b/release/scripts/ui/properties_data_camera.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class DataButtonsPanel(bpy.types.Panel): @@ -29,12 +29,14 @@ class DataButtonsPanel(bpy.types.Panel): bl_context = "data" def poll(self, context): - return context.camera + engine = context.scene.render.engine + return context.camera and (engine in self.COMPAT_ENGINES) class DATA_PT_context_camera(DataButtonsPanel): bl_label = "" bl_show_header = False + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -61,10 +63,12 @@ class DATA_PT_context_camera(DataButtonsPanel): class DATA_PT_custom_props_camera(DataButtonsPanel, PropertyPanel): _context_path = "object.data" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} class DATA_PT_camera(DataButtonsPanel): bl_label = "Lens" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -125,6 +129,7 @@ class DATA_PT_camera(DataButtonsPanel): class DATA_PT_camera_display(DataButtonsPanel): bl_label = "Display" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout diff --git a/release/scripts/ui/properties_data_curve.py b/release/scripts/ui/properties_data_curve.py index 9848e423665..fd836f5e5d4 100644 --- a/release/scripts/ui/properties_data_curve.py +++ b/release/scripts/ui/properties_data_curve.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class DataButtonsPanel(bpy.types.Panel): @@ -44,7 +44,7 @@ class DataButtonsPanelActive(DataButtonsPanel): def poll(self, context): curve = context.curve - return (curve and curve.active_spline) + return (curve and type(curve) is not bpy.types.TextCurve and curve.splines.active) class DATA_PT_context_curve(DataButtonsPanel): @@ -120,13 +120,13 @@ class DATA_PT_shape_curve(DataButtonsPanel): if is_curve or is_text: sub = col.column() - sub.active = (curve.dimensions == '2D') sub.label(text="Caps:") sub.prop(curve, "front") sub.prop(curve, "back") + sub.prop(curve, "use_deform_fill") col.label(text="Textures:") -# col.prop(curve, "uv_orco") + col.prop(curve, "map_along_length") col.prop(curve, "auto_texspace") @@ -204,7 +204,7 @@ class DATA_PT_active_spline(DataButtonsPanelActive): ob = context.object curve = context.curve - act_spline = curve.active_spline + act_spline = curve.splines.active is_surf = (ob.type == 'SURFACE') is_poly = (act_spline.type == 'POLY') @@ -308,10 +308,11 @@ class DATA_PT_font(DataButtonsPanel): split = layout.split() - col = split.column(align=True) - col.label(text="Underline:") - col.prop(text, "ul_position", text="Position") - col.prop(text, "ul_height", text="Thickness") + col = split.column() + colsub = col.column(align=True) + colsub.label(text="Underline:") + colsub.prop(text, "ul_position", text="Position") + colsub.prop(text, "ul_height", text="Thickness") if wide_ui: col = split.column() @@ -319,8 +320,13 @@ class DATA_PT_font(DataButtonsPanel): col.prop(char, "bold") col.prop(char, "italic") col.prop(char, "underline") -# col.prop(char, "style") -# col.prop(char, "wrap") + + split = layout.split() + col = split.column() + col.prop(text, "small_caps_scale", text="Small Caps") + + col = split.column() + col.prop(char, "use_small_caps") class DATA_PT_paragraph(DataButtonsPanel): @@ -368,20 +374,35 @@ class DATA_PT_textboxes(DataButtonsPanel): text = context.curve wide_ui = context.region.width > narrowui - for box in text.textboxes: - split = layout.box().split() + split = layout.split() + col = split.column() + col.operator("font.textbox_add", icon='ZOOMIN') + if wide_ui: + col = split.column() + + for i, box in enumerate(text.textboxes): + + boxy = layout.box() + + row = boxy.row() + + split = row.split() col = split.column(align=True) + col.label(text="Dimensions:") col.prop(box, "width", text="Width") col.prop(box, "height", text="Height") if wide_ui: col = split.column(align=True) + col.label(text="Offset:") col.prop(box, "x", text="X") col.prop(box, "y", text="Y") + row.operator("font.textbox_remove", text='', icon='X', emboss=False).index = i + classes = [ DATA_PT_context_curve, diff --git a/release/scripts/ui/properties_data_empty.py b/release/scripts/ui/properties_data_empty.py index a0999fb9d4a..577e32ed840 100644 --- a/release/scripts/ui/properties_data_empty.py +++ b/release/scripts/ui/properties_data_empty.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class DataButtonsPanel(bpy.types.Panel): diff --git a/release/scripts/ui/properties_data_lamp.py b/release/scripts/ui/properties_data_lamp.py index 9a99959a610..14493301b1e 100644 --- a/release/scripts/ui/properties_data_lamp.py +++ b/release/scripts/ui/properties_data_lamp.py @@ -20,13 +20,14 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class LAMP_MT_sunsky_presets(bpy.types.Menu): - bl_label = "Render Presets" + bl_label = "Sun & Sky Presets" preset_subdir = "sunsky" - preset_operator = "script.python_file_run" + preset_operator = "script.execute_preset" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} draw = bpy.types.Menu.draw_preset @@ -36,11 +37,13 @@ class DataButtonsPanel(bpy.types.Panel): bl_context = "data" def poll(self, context): - return context.lamp + engine = context.scene.render.engine + return context.lamp and (engine in self.COMPAT_ENGINES) class DATA_PT_preview(DataButtonsPanel): bl_label = "Preview" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): self.layout.template_preview(context.lamp) @@ -49,6 +52,7 @@ class DATA_PT_preview(DataButtonsPanel): class DATA_PT_context_lamp(DataButtonsPanel): bl_label = "" bl_show_header = False + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -75,10 +79,12 @@ class DATA_PT_context_lamp(DataButtonsPanel): class DATA_PT_custom_props_lamp(DataButtonsPanel, PropertyPanel): _context_path = "object.data" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} class DATA_PT_lamp(DataButtonsPanel): bl_label = "Lamp" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -125,10 +131,12 @@ class DATA_PT_lamp(DataButtonsPanel): class DATA_PT_sunsky(DataButtonsPanel): bl_label = "Sky & Atmosphere" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): lamp = context.lamp - return (lamp and lamp.type == 'SUN') + engine = context.scene.render.engine + return (lamp and lamp.type == 'SUN') and (engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -138,8 +146,8 @@ class DATA_PT_sunsky(DataButtonsPanel): row = layout.row(align=True) row.prop(lamp, "use_sky") - row.menu("LAMP_MT_sunsky_presets", text="Presets") - row.operator("lamp.sunsky_preset_add", text="Add") + row.menu("LAMP_MT_sunsky_presets", text=bpy.types.LAMP_MT_sunsky_presets.bl_label) + row.operator("lamp.sunsky_preset_add", text="", icon="ZOOMIN") row = layout.row() row.active = lamp.use_sky or lamp.use_atmosphere @@ -196,10 +204,12 @@ class DATA_PT_sunsky(DataButtonsPanel): class DATA_PT_shadow(DataButtonsPanel): bl_label = "Shadow" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): lamp = context.lamp - return (lamp and lamp.type in ('POINT', 'SUN', 'SPOT', 'AREA')) + engine = context.scene.render.engine + return (lamp and lamp.type in ('POINT', 'SUN', 'SPOT', 'AREA')) and (engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -319,10 +329,12 @@ class DATA_PT_shadow(DataButtonsPanel): class DATA_PT_area(DataButtonsPanel): bl_label = "Area Shape" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): lamp = context.lamp - return (lamp and lamp.type == 'AREA') + engine = context.scene.render.engine + return (lamp and lamp.type == 'AREA') and (engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -344,10 +356,12 @@ class DATA_PT_area(DataButtonsPanel): class DATA_PT_spot(DataButtonsPanel): bl_label = "Spot Shape" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): lamp = context.lamp - return (lamp and lamp.type == 'SPOT') + engine = context.scene.render.engine + return (lamp and lamp.type == 'SPOT') and (engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -379,11 +393,13 @@ class DATA_PT_spot(DataButtonsPanel): class DATA_PT_falloff_curve(DataButtonsPanel): bl_label = "Falloff Curve" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): lamp = context.lamp + engine = context.scene.render.engine - return (lamp and lamp.type in ('POINT', 'SPOT') and lamp.falloff_type == 'CUSTOM_CURVE') + return (lamp and lamp.type in ('POINT', 'SPOT') and lamp.falloff_type == 'CUSTOM_CURVE') and (engine in self.COMPAT_ENGINES) def draw(self, context): lamp = context.lamp diff --git a/release/scripts/ui/properties_data_lattice.py b/release/scripts/ui/properties_data_lattice.py index 0c1f9d8001c..2aa719437a7 100644 --- a/release/scripts/ui/properties_data_lattice.py +++ b/release/scripts/ui/properties_data_lattice.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class DataButtonsPanel(bpy.types.Panel): @@ -93,7 +93,9 @@ class DATA_PT_lattice(DataButtonsPanel): col = split.column() col.prop(lat, "interpolation_type_w", text="") - layout.prop(lat, "outside") + row = layout.row() + row.prop(lat, "outside") + row.prop_object(lat, "vertex_group", context.object, "vertex_groups", text="") classes = [ diff --git a/release/scripts/ui/properties_data_mesh.py b/release/scripts/ui/properties_data_mesh.py index d48d2ec8046..44d3d19e793 100644 --- a/release/scripts/ui/properties_data_mesh.py +++ b/release/scripts/ui/properties_data_mesh.py @@ -20,11 +20,12 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class MESH_MT_vertex_group_specials(bpy.types.Menu): bl_label = "Vertex Group Specials" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -38,6 +39,7 @@ class MESH_MT_vertex_group_specials(bpy.types.Menu): class MESH_MT_shape_key_specials(bpy.types.Menu): bl_label = "Shape Key Specials" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -53,12 +55,14 @@ class DataButtonsPanel(bpy.types.Panel): bl_context = "data" def poll(self, context): - return context.mesh + engine = context.scene.render.engine + return context.mesh and (engine in self.COMPAT_ENGINES) class DATA_PT_context_mesh(DataButtonsPanel): bl_label = "" bl_show_header = False + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -85,10 +89,12 @@ class DATA_PT_context_mesh(DataButtonsPanel): class DATA_PT_custom_props_mesh(DataButtonsPanel, PropertyPanel): _context_path = "object.data" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} class DATA_PT_normals(DataButtonsPanel): bl_label = "Normals" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -114,6 +120,7 @@ class DATA_PT_normals(DataButtonsPanel): class DATA_PT_settings(DataButtonsPanel): bl_label = "Settings" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -125,9 +132,11 @@ class DATA_PT_settings(DataButtonsPanel): class DATA_PT_vertex_groups(DataButtonsPanel): bl_label = "Vertex Groups" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): - return (context.object and context.object.type in ('MESH', 'LATTICE')) + engine = context.scene.render.engine + return (context.object and context.object.type in ('MESH', 'LATTICE') and (engine in self.COMPAT_ENGINES)) def draw(self, context): layout = self.layout @@ -146,6 +155,9 @@ class DATA_PT_vertex_groups(DataButtonsPanel): col.operator("object.vertex_group_add", icon='ZOOMIN', text="") col.operator("object.vertex_group_remove", icon='ZOOMOUT', text="") col.menu("MESH_MT_vertex_group_specials", icon='DOWNARROW_HLT', text="") + if group: + col.operator("object.vertex_group_move", icon='TRIA_UP', text="").direction = 'UP' + col.operator("object.vertex_group_move", icon='TRIA_DOWN', text="").direction = 'DOWN' if group: row = layout.row() @@ -167,9 +179,11 @@ class DATA_PT_vertex_groups(DataButtonsPanel): class DATA_PT_shape_keys(DataButtonsPanel): bl_label = "Shape Keys" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): - return (context.object and context.object.type in ('MESH', 'LATTICE', 'CURVE', 'SURFACE')) + engine = context.scene.render.engine + return (context.object and context.object.type in ('MESH', 'LATTICE', 'CURVE', 'SURFACE') and (engine in self.COMPAT_ENGINES)) def draw(self, context): layout = self.layout @@ -263,6 +277,7 @@ class DATA_PT_shape_keys(DataButtonsPanel): class DATA_PT_uv_texture(DataButtonsPanel): bl_label = "UV Texture" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -283,8 +298,56 @@ class DATA_PT_uv_texture(DataButtonsPanel): layout.prop(lay, "name") +class DATA_PT_texface(DataButtonsPanel): + bl_label = "Texture Face" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def poll(self, context): + ob = context.active_object + rd = context.scene.render + + return (context.mode == 'EDIT_MESH') and (rd.engine == 'BLENDER_GAME') and ob and ob.type == 'MESH' + + def draw(self, context): + layout = self.layout + col = layout.column() + + wide_ui = context.region.width > narrowui + me = context.mesh + + tf = me.faces.active_tface + + if tf: + split = layout.split() + col = split.column() + + col.prop(tf, "tex") + col.prop(tf, "light") + col.prop(tf, "invisible") + col.prop(tf, "collision") + + col.prop(tf, "shared") + col.prop(tf, "twoside") + col.prop(tf, "object_color") + + if wide_ui: + col = split.column() + + col.prop(tf, "halo") + col.prop(tf, "billboard") + col.prop(tf, "shadow") + col.prop(tf, "text") + col.prop(tf, "alpha_sort") + + col = layout.column() + col.prop(tf, "transp") + else: + col.label(text="No UV Texture") + + class DATA_PT_vertex_colors(DataButtonsPanel): bl_label = "Vertex Colors" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -315,6 +378,7 @@ classes = [ DATA_PT_vertex_groups, DATA_PT_shape_keys, DATA_PT_uv_texture, + DATA_PT_texface, DATA_PT_vertex_colors, DATA_PT_custom_props_mesh] diff --git a/release/scripts/ui/properties_data_metaball.py b/release/scripts/ui/properties_data_metaball.py index b751a9f9059..e0155059b98 100644 --- a/release/scripts/ui/properties_data_metaball.py +++ b/release/scripts/ui/properties_data_metaball.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class DataButtonsPanel(bpy.types.Panel): diff --git a/release/scripts/ui/properties_data_modifier.py b/release/scripts/ui/properties_data_modifier.py index 8364d070d1a..dfa0d8029b2 100644 --- a/release/scripts/ui/properties_data_modifier.py +++ b/release/scripts/ui/properties_data_modifier.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check narrowmod = 260 @@ -164,7 +164,7 @@ class DATA_PT_modifiers(DataButtonsPanel): split = layout.split() col = split.column() - col.prop(md, "start") + col.prop(md, "frame_start") col.prop(md, "length") if wide_ui: @@ -229,7 +229,7 @@ class DATA_PT_modifiers(DataButtonsPanel): def DECIMATE(self, layout, ob, md, wide_ui): layout.prop(md, "ratio") - layout.prop(md, "face_count") + layout.label(text="Face Count: %s" % str(md.face_count)) def DISPLACE(self, layout, ob, md, wide_ui): split = layout.split() @@ -291,6 +291,7 @@ class DATA_PT_modifiers(DataButtonsPanel): col.prop(md, "unborn") col.prop(md, "alive") col.prop(md, "dead") + col.prop(md, "size") layout.operator("object.explode_refresh", text="Refresh") @@ -368,7 +369,6 @@ class DATA_PT_modifiers(DataButtonsPanel): sub = col.column() sub.label(text="Object:") sub.prop(md, "object", text="") - sub.prop(md, "mode", text="") sub.active = not md.is_bound if wide_ui: col = split.column() @@ -386,15 +386,14 @@ class DATA_PT_modifiers(DataButtonsPanel): else: layout.operator("object.meshdeform_bind", text="Bind") - if md.mode == 'VOLUME': - split = layout.split() + split = layout.split() - col = split.column() - col.prop(md, "precision") + col = split.column() + col.prop(md, "precision") - if wide_ui: - col = split.column() - col.prop(md, "dynamic") + if wide_ui: + col = split.column() + col.prop(md, "dynamic") def MIRROR(self, layout, ob, md, wide_ui): layout.prop(md, "merge_limit") @@ -438,6 +437,7 @@ class DATA_PT_modifiers(DataButtonsPanel): col.prop(md, "levels", text="Preview") col.prop(md, "sculpt_levels", text="Sculpt") col.prop(md, "render_levels", text="Render") + col.prop(bpy.context.tool_settings.sculpt, "fast_navigate") if wide_ui: col = split.column() @@ -453,12 +453,12 @@ class DATA_PT_modifiers(DataButtonsPanel): col = layout.column() row = col.row() if md.external: - row.operator("object.multires_pack_external", text="Pack External") + row.operator("object.multires_external_pack", text="Pack External") row.label() row = col.row() - row.prop(md, "filename", text="") + row.prop(md, "filepath", text="") else: - row.operator("object.multires_save_external", text="Save External...") + row.operator("object.multires_external_save", text="Save External...") row.label() def PARTICLE_INSTANCE(self, layout, ob, md, wide_ui): @@ -481,10 +481,10 @@ class DATA_PT_modifiers(DataButtonsPanel): layout.separator() - layout.prop(md, "path", text="Create Along Paths") + layout.prop(md, "use_path", text="Create Along Paths") split = layout.split() - split.active = md.path + split.active = md.use_path col = split.column() col.row().prop(md, "axis", expand=True) col.prop(md, "keep_shape") @@ -497,6 +497,28 @@ class DATA_PT_modifiers(DataButtonsPanel): def PARTICLE_SYSTEM(self, layout, ob, md, wide_ui): layout.label(text="See Particle panel.") + def SCREW(self, layout, ob, md, wide_ui): + split = layout.split() + + col = split.column() + col.prop(md, "axis") + col.prop(md, "object", text="AxisOb") + col.prop(md, "angle") + col.prop(md, "steps") + col.prop(md, "render_steps") + + if wide_ui: + col = split.column() + row = col.row() + row.active = (md.object is None or md.use_object_screw_offset == False) + row.prop(md, "screw_offset") + row = col.row() + row.active = (md.object is not None) + row.prop(md, "use_object_screw_offset") + col.prop(md, "use_normal_calculate") + col.prop(md, "use_normal_flip") + col.prop(md, "iterations") + def SHRINKWRAP(self, layout, ob, md, wide_ui): split = layout.split() col = split.column() @@ -602,11 +624,13 @@ class DATA_PT_modifiers(DataButtonsPanel): layout.label(text="See Soft Body panel.") def SOLIDIFY(self, layout, ob, md, wide_ui): - layout.prop(md, "offset") split = layout.split() col = split.column() + col.prop(md, "thickness") + col.prop_object(md, "vertex_group", ob, "vertex_groups", text="") + col.label(text="Crease:") col.prop(md, "edge_crease_inner", text="Inner") col.prop(md, "edge_crease_outer", text="Outer") @@ -614,11 +638,20 @@ class DATA_PT_modifiers(DataButtonsPanel): if wide_ui: col = split.column() - col.label() - col.prop(md, "use_rim") + + col.prop(md, "offset") + colsub = col.column() + colsub.active = (md.vertex_group is not "") + colsub.prop(md, "invert", 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") + # col = layout.column() # col.label(text="Vertex Group:") # col.prop_object(md, "vertex_group", ob, "vertex_groups", text="") @@ -638,6 +671,7 @@ class DATA_PT_modifiers(DataButtonsPanel): if wide_ui: col = split.column() col.label(text="Options:") + col.prop(md, "subsurf_uv") col.prop(md, "optimal_display") def SURFACE(self, layout, ob, md, wide_ui): @@ -666,9 +700,12 @@ class DATA_PT_modifiers(DataButtonsPanel): if wide_ui: col = split.column() sub = col.column(align=True) - sub.label(text="Aspect Ratio:") - sub.prop(md, "horizontal_aspect_ratio", text="Horizontal") - sub.prop(md, "vertical_aspect_ratio", text="Vertical") + sub.prop(md, "aspect_x", text="Aspect X") + sub.prop(md, "aspect_y", text="Aspect Y") + + sub = col.column(align=True) + sub.prop(md, "scale_x", text="Scale X") + sub.prop(md, "scale_y", text="Scale Y") def WAVE(self, layout, ob, md, wide_ui): split = layout.split() diff --git a/release/scripts/ui/properties_game.py b/release/scripts/ui/properties_game.py index 41af955d9ec..9ec7168f834 100644 --- a/release/scripts/ui/properties_game.py +++ b/release/scripts/ui/properties_game.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class PhysicsButtonsPanel(bpy.types.Panel): @@ -30,11 +30,12 @@ class PhysicsButtonsPanel(bpy.types.Panel): def poll(self, context): ob = context.active_object rd = context.scene.render - return ob and ob.game and (rd.engine == 'BLENDER_GAME') + return ob and ob.game and (rd.engine in self.COMPAT_ENGINES) class PHYSICS_PT_game_physics(PhysicsButtonsPanel): bl_label = "Physics" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -163,11 +164,12 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel): class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel): bl_label = "Collision Bounds" + COMPAT_ENGINES = {'BLENDER_GAME'} def poll(self, context): game = context.object.game rd = context.scene.render - return (game.physics_type in ('DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC')) and (rd.engine == 'BLENDER_GAME') + return (game.physics_type in ('DYNAMIC', 'RIGID_BODY', 'SENSOR', 'SOFT_BODY', 'STATIC')) and (rd.engine in self.COMPAT_ENGINES) def draw_header(self, context): game = context.active_object.game @@ -203,11 +205,12 @@ class RenderButtonsPanel(bpy.types.Panel): def poll(self, context): rd = context.scene.render - return (rd.engine == 'BLENDER_GAME') + return (rd.engine in self.COMPAT_ENGINES) class RENDER_PT_game(RenderButtonsPanel): bl_label = "Game" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -219,6 +222,7 @@ class RENDER_PT_game(RenderButtonsPanel): class RENDER_PT_game_player(RenderButtonsPanel): bl_label = "Standalone Player" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -256,6 +260,7 @@ class RENDER_PT_game_player(RenderButtonsPanel): class RENDER_PT_game_stereo(RenderButtonsPanel): bl_label = "Stereo" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -315,6 +320,7 @@ class RENDER_PT_game_stereo(RenderButtonsPanel): class RENDER_PT_game_shading(RenderButtonsPanel): bl_label = "Shading" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -343,6 +349,7 @@ class RENDER_PT_game_shading(RenderButtonsPanel): class RENDER_PT_game_performance(RenderButtonsPanel): bl_label = "Performance" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -368,6 +375,7 @@ class RENDER_PT_game_performance(RenderButtonsPanel): class RENDER_PT_game_sound(RenderButtonsPanel): bl_label = "Sound" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -390,12 +398,13 @@ class WorldButtonsPanel(bpy.types.Panel): def poll(self, context): scene = context.scene - return (scene.render.engine == 'BLENDER_GAME') and (scene.world is not None) + return (scene.render.engine in self.COMPAT_ENGINES) and (scene.world is not None) class WORLD_PT_game_context_world(WorldButtonsPanel): bl_label = "" bl_show_header = False + COMPAT_ENGINES = {'BLENDER_GAME'} def poll(self, context): rd = context.scene.render @@ -424,6 +433,7 @@ class WORLD_PT_game_context_world(WorldButtonsPanel): class WORLD_PT_game_world(WorldButtonsPanel): bl_label = "World" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -443,11 +453,12 @@ class WORLD_PT_game_world(WorldButtonsPanel): class WORLD_PT_game_mist(WorldButtonsPanel): bl_label = "Mist" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw_header(self, context): world = context.world - self.layout.prop(world.mist, "enabled", text="") + self.layout.prop(world.mist, "use_mist", text="") def draw(self, context): layout = self.layout @@ -455,7 +466,7 @@ class WORLD_PT_game_mist(WorldButtonsPanel): world = context.world wide_ui = context.region.width > narrowui - layout.active = world.mist.enabled + layout.active = world.mist.use_mist split = layout.split() col = split.column() @@ -468,6 +479,7 @@ class WORLD_PT_game_mist(WorldButtonsPanel): class WORLD_PT_game_physics(WorldButtonsPanel): bl_label = "Physics" + COMPAT_ENGINES = {'BLENDER_GAME'} def draw(self, context): layout = self.layout diff --git a/release/scripts/ui/properties_material.py b/release/scripts/ui/properties_material.py index 13bfd7a7f5d..1526bd004a0 100644 --- a/release/scripts/ui/properties_material.py +++ b/release/scripts/ui/properties_material.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check def active_node_mat(mat): @@ -39,7 +39,7 @@ def active_node_mat(mat): class MATERIAL_MT_sss_presets(bpy.types.Menu): bl_label = "SSS Presets" preset_subdir = "sss" - preset_operator = "script.python_file_run" + preset_operator = "script.execute_preset" draw = bpy.types.Menu.draw_preset @@ -505,8 +505,8 @@ class MATERIAL_PT_sss(MaterialButtonsPanel): row = layout.row().split() sub = row.row(align=True).split(percentage=0.75) - sub.menu("MATERIAL_MT_sss_presets", text="Presets") - sub.operator("material.sss_preset_add", text="Add") + sub.menu("MATERIAL_MT_sss_presets", text=bpy.types.MATERIAL_MT_sss_presets.bl_label) + sub.operator("material.sss_preset_add", text="", icon="ZOOMIN") split = layout.split() @@ -570,10 +570,11 @@ class MATERIAL_PT_mirror(MaterialButtonsPanel): col = split.column() col.separator() - col.prop(raym, "distance", text="Max Dist") col.prop(raym, "depth") + col.prop(raym, "distance", text="Max Dist") col.separator() sub = col.split(percentage=0.4) + sub.active = raym.distance > 0.0 sub.label(text="Fade To:") sub.prop(raym, "fade_to", text="") @@ -913,6 +914,32 @@ class MATERIAL_PT_volume_integration(VolumeButtonsPanel): col.prop(vol, "depth_cutoff") +class MATERIAL_PT_volume_options(VolumeButtonsPanel): + bl_label = "Options" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + bl_default_closed = True + + def draw(self, context): + layout = self.layout + + mat = active_node_mat(context.material) + wide_ui = context.region.width > narrowui + + split = layout.split() + + col = split.column() + col.prop(mat, "traceable") + col.prop(mat, "full_oversampling") + col.prop(mat, "exclude_mist") + + col = split.column() + col.label(text="Light Group:") + col.prop(mat, "light_group", text="") + row = col.row() + row.active = bool(mat.light_group) + row.prop(mat, "light_group_exclusive", text="Exclusive") + + classes = [ MATERIAL_PT_context_material, MATERIAL_PT_preview, @@ -937,8 +964,8 @@ classes = [ MATERIAL_PT_volume_shading, MATERIAL_PT_volume_lighting, MATERIAL_PT_volume_transp, - MATERIAL_PT_volume_integration, + MATERIAL_PT_volume_options, MATERIAL_PT_custom_props] diff --git a/release/scripts/ui/properties_object.py b/release/scripts/ui/properties_object.py index b11180411f4..4e60879d665 100644 --- a/release/scripts/ui/properties_object.py +++ b/release/scripts/ui/properties_object.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class ObjectButtonsPanel(bpy.types.Panel): @@ -35,12 +35,15 @@ class OBJECT_PT_context_object(ObjectButtonsPanel): def draw(self, context): layout = self.layout - + space = context.space_data ob = context.object row = layout.row() row.label(text="", icon='OBJECT_DATA') - row.prop(ob, "name", text="") + if space.use_pin_id: + row.template_ID(space, "pin_id") + else: + row.prop(ob, "name", text="") class OBJECT_PT_custom_props(ObjectButtonsPanel, PropertyPanel): @@ -152,13 +155,11 @@ class OBJECT_PT_groups(ObjectButtonsPanel): ob = context.object wide_ui = context.region.width > narrowui - if wide_ui: - split = layout.split() - split.operator_menu_enum("object.group_add", "group") - split.label() - else: - layout.operator_menu_enum("object.group_add", "group") + row = layout.row(align=True) + row.operator("object.group_link", text="Add to Group") + row.operator("object.group_add", text="", icon='ZOOMIN') + # XXX, this is bad practice, yes, I wrote it :( - campbell index = 0 value = str(tuple(context.scene.cursor_location)) for group in bpy.data.groups: @@ -169,7 +170,7 @@ class OBJECT_PT_groups(ObjectButtonsPanel): row = col.box().row() row.prop(group, "name", text="") - row.operator("object.group_remove", text="", icon='X') + row.operator("object.group_remove", text="", icon='X', emboss=False) split = col.box().split() @@ -181,7 +182,7 @@ class OBJECT_PT_groups(ObjectButtonsPanel): col.prop(group, "dupli_offset", text="") prop = col.operator("wm.context_set_value", text="From Cursor") - prop.path = "object.group_users[%d].dupli_offset" % index + prop.data_path = "object.users_group[%d].dupli_offset" % index prop.value = value index += 1 @@ -213,6 +214,7 @@ class OBJECT_PT_display(ObjectButtonsPanel): col.prop(ob, "draw_name", text="Name") col.prop(ob, "draw_axis", text="Axis") col.prop(ob, "draw_wire", text="Wire") + col.prop(ob, "color", text="Object Color") if wide_ui: col = split.column() @@ -268,9 +270,12 @@ class OBJECT_PT_duplication(ObjectButtonsPanel): else: layout.prop(ob, "dupli_group", text="") +# XXX: the following options are all quite buggy, ancient hacks that should be dropped + class OBJECT_PT_animation(ObjectButtonsPanel): - bl_label = "Animation" + bl_label = "Animation Hacks" + bl_default_closed = True def draw(self, context): layout = self.layout @@ -294,17 +299,15 @@ class OBJECT_PT_animation(ObjectButtonsPanel): row.active = (ob.parent is not None) col.prop(ob, "time_offset", text="Offset") + # XXX: these are still used for a few curve-related tracking features if wide_ui: col = split.column() - col.label(text="Track:") - col.prop(ob, "track", text="") + col.label(text="Tracking Axes:") col.prop(ob, "track_axis", text="Axis") col.prop(ob, "up_axis", text="Up Axis") - row = col.row() - row.prop(ob, "track_override_parent", text="Override Parent") - row.active = (ob.parent is not None) -# import generic panels from other files + +# import generic panels from other files from properties_animviz import OBJECT_PT_motion_paths, OBJECT_PT_onion_skinning classes = [ @@ -315,8 +318,8 @@ classes = [ OBJECT_PT_groups, OBJECT_PT_display, OBJECT_PT_duplication, - OBJECT_PT_animation, - + OBJECT_PT_animation, # XXX: panel of old hacks pending to be removed... + OBJECT_PT_motion_paths, #OBJECT_PT_onion_skinning, diff --git a/release/scripts/ui/properties_object_constraint.py b/release/scripts/ui/properties_object_constraint.py index 6f6fcfc1641..335bd25b501 100644 --- a/release/scripts/ui/properties_object_constraint.py +++ b/release/scripts/ui/properties_object_constraint.py @@ -19,7 +19,8 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check +narrowcon = 260 class ConstraintButtonsPanel(bpy.types.Panel): @@ -30,14 +31,15 @@ class ConstraintButtonsPanel(bpy.types.Panel): def draw_constraint(self, context, con): layout = self.layout - box = layout.template_constraint(con) wide_ui = context.region.width > narrowui + compact_con = context.region.width < narrowcon + box = layout.template_constraint(con, compact=compact_con) if box: # match enum type to our functions, avoids a lookup table. getattr(self, con.type)(context, box, con, wide_ui) - if con.type not in ('RIGID_BODY_JOINT', 'SPLINE_IK', 'NULL'): + if con.type not in ('RIGID_BODY_JOINT', 'NULL'): box.prop(con, "influence") def space_template(self, layout, con, wide_ui, target=True, owner=True): @@ -76,7 +78,7 @@ class ConstraintButtonsPanel(bpy.types.Panel): else: layout.prop_object(con, "subtarget", con.target.data, "bones", text="") - if con.type in ('COPY_LOCATION', 'STRETCH_TO', 'TRACK_TO'): + if con.type in ('COPY_LOCATION', 'STRETCH_TO', 'TRACK_TO', 'PIVOT'): row = layout.row() row.label(text="Head/Tail:") row.prop(con, "head_tail", text="") @@ -97,12 +99,12 @@ class ConstraintButtonsPanel(bpy.types.Panel): split = layout.split(percentage=0.33) col = split.column() - col.prop(con, "tail") - col.prop(con, "stretch") + col.prop(con, "use_tail") + col.prop(con, "use_stretch") col = split.column() col.prop(con, "chain_length") - col.prop(con, "targetless") + col.prop(con, "use_target") def CHILD_OF(self, context, layout, con, wide_ui): self.target_template(layout, con, wide_ui) @@ -205,10 +207,10 @@ class ConstraintButtonsPanel(bpy.types.Panel): row.label(text="Axis Ref:") row.prop(con, "axis_reference", expand=True) split = layout.split(percentage=0.33) - split.row().prop(con, "position") + split.row().prop(con, "use_position") row = split.row() row.prop(con, "weight", text="Weight", slider=True) - row.active = con.position + row.active = con.use_position split = layout.split(percentage=0.33) row = split.row() row.label(text="Lock:") @@ -219,7 +221,7 @@ class ConstraintButtonsPanel(bpy.types.Panel): split.active = con.use_position split = layout.split(percentage=0.33) - split.row().prop(con, "rotation") + split.row().prop(con, "use_rotation") row = split.row() row.prop(con, "orient_weight", text="Weight", slider=True) row.active = con.use_rotation @@ -461,6 +463,17 @@ class ConstraintButtonsPanel(bpy.types.Panel): self.space_template(layout, con, wide_ui) + def MAINTAIN_VOLUME(self, context, layout, con, wide_ui): + + row = layout.row() + if wide_ui: + row.label(text="Free:") + row.prop(con, "axis", expand=True) + + layout.prop(con, "volume") + + self.space_template(layout, con, wide_ui) + def COPY_TRANSFORMS(self, context, layout, con, wide_ui): self.target_template(layout, con, wide_ui) @@ -485,8 +498,8 @@ class ConstraintButtonsPanel(bpy.types.Panel): col = split.column(align=True) col.label(text="Action Length:") - col.prop(con, "start_frame", text="Start") - col.prop(con, "end_frame", text="End") + col.prop(con, "frame_start", text="Start") + col.prop(con, "frame_end", text="End") if wide_ui: col = split.column(align=True) @@ -719,6 +732,23 @@ class ConstraintButtonsPanel(bpy.types.Panel): col.prop(con, "xz_scaling_mode", text="") col.prop(con, "use_curve_radius") + def PIVOT(self, context, layout, con, wide_ui): + self.target_template(layout, con, wide_ui) + + if con.target: + col = layout.column() + col.prop(con, "offset", text="Pivot Offset") + else: + col = layout.column() + col.prop(con, "use_relative_position") + if con.use_relative_position: + col.prop(con, "offset", text="Relative Pivot Point") + else: + col.prop(con, "offset", text="Absolute Pivot Point") + + col = layout.column() + col.prop(con, "enabled_rotation_range", text="Pivot When") + class OBJECT_PT_constraints(ConstraintButtonsPanel): bl_label = "Object Constraints" diff --git a/release/scripts/ui/properties_particle.py b/release/scripts/ui/properties_particle.py index dbd2b90fea2..58e602dd1e2 100644 --- a/release/scripts/ui/properties_particle.py +++ b/release/scripts/ui/properties_particle.py @@ -25,18 +25,21 @@ 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 +narrowui = bpy.context.user_preferences.view.properties_width_check + def particle_panel_enabled(context, psys): return (psys.point_cache.baked is False) and (not psys.edited) and (not context.particle_system_editable) -def particle_panel_poll(context): +def particle_panel_poll(panel, context): psys = context.particle_system + engine = context.scene.render.engine if psys is None: return False if psys.settings is None: return False - return psys.settings.type in ('EMITTER', 'REACTOR', 'HAIR') + return psys.settings.type in ('EMITTER', 'REACTOR', 'HAIR') and (engine in panel.COMPAT_ENGINES) class ParticleButtonsPanel(bpy.types.Panel): @@ -45,15 +48,17 @@ class ParticleButtonsPanel(bpy.types.Panel): bl_context = "particle" def poll(self, context): - return particle_panel_poll(context) + return particle_panel_poll(self, context) class PARTICLE_PT_context_particles(ParticleButtonsPanel): bl_label = "" bl_show_header = False + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): - return (context.particle_system or context.object) + engine = context.scene.render.engine + return (context.particle_system or context.object) and (engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -132,14 +137,16 @@ class PARTICLE_PT_context_particles(ParticleButtonsPanel): class PARTICLE_PT_custom_props(ParticleButtonsPanel, PropertyPanel): + COMPAT_ENGINES = {'BLENDER_RENDER'} _context_path = "particle_system.settings" class PARTICLE_PT_emission(ParticleButtonsPanel): bl_label = "Emission" + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): - if particle_panel_poll(context): + if particle_panel_poll(self, context): return not context.particle_system.point_cache.external else: return False @@ -149,6 +156,7 @@ class PARTICLE_PT_emission(ParticleButtonsPanel): psys = context.particle_system part = psys.settings + wide_ui = context.region.width > narrowui layout.enabled = particle_panel_enabled(context, psys) and not psys.multiple_caches @@ -160,8 +168,8 @@ class PARTICLE_PT_emission(ParticleButtonsPanel): split = layout.split() col = split.column(align=True) - col.prop(part, "start") - col.prop(part, "end") + col.prop(part, "frame_start") + col.prop(part, "frame_end") col = split.column(align=True) col.prop(part, "lifetime") @@ -170,7 +178,10 @@ class PARTICLE_PT_emission(ParticleButtonsPanel): layout.row().label(text="Emit From:") row = layout.row() - row.prop(part, "emit_from", expand=True) + if wide_ui: + row.prop(part, "emit_from", expand=True) + else: + row.prop(part, "emit_from", text="") row = layout.row() row.prop(part, "trand") if part.distribution != 'GRID': @@ -178,7 +189,10 @@ class PARTICLE_PT_emission(ParticleButtonsPanel): if part.emit_from == 'FACE' or part.emit_from == 'VOLUME': row = layout.row() - row.prop(part, "distribution", expand=True) + if wide_ui: + row.prop(part, "distribution", expand=True) + else: + row.prop(part, "distribution", text="") row = layout.row() @@ -192,14 +206,16 @@ class PARTICLE_PT_emission(ParticleButtonsPanel): class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel): bl_label = "Hair dynamics" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): psys = context.particle_system + engine = context.scene.render.engine if psys is None: return False if psys.settings is None: return False - return psys.settings.type == 'HAIR' + return psys.settings.type == 'HAIR' and (engine in self.COMPAT_ENGINES) def draw_header(self, context): #cloth = context.cloth.collision_settings @@ -247,9 +263,11 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel): class PARTICLE_PT_cache(ParticleButtonsPanel): bl_label = "Cache" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): psys = context.particle_system + engine = context.scene.render.engine if psys is None: return False if psys.settings is None: @@ -257,20 +275,20 @@ class PARTICLE_PT_cache(ParticleButtonsPanel): 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.hair_dynamics) + return (psys.settings.type in ('EMITTER', 'REACTOR') or (psys.settings.type == 'HAIR' and psys.hair_dynamics)) and engine in self.COMPAT_ENGINES def draw(self, context): - psys = context.particle_system - point_cache_ui(self, context, psys.point_cache, particle_panel_enabled(context, psys), not psys.hair_dynamics, 0) + point_cache_ui(self, context, psys.point_cache, True, 'HAIR' if psys.hair_dynamics else 'PSYS') class PARTICLE_PT_velocity(ParticleButtonsPanel): bl_label = "Velocity" + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): - if particle_panel_poll(context): + if particle_panel_poll(self, context): psys = context.particle_system return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.external else: @@ -314,9 +332,10 @@ class PARTICLE_PT_velocity(ParticleButtonsPanel): class PARTICLE_PT_rotation(ParticleButtonsPanel): bl_label = "Rotation" + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): - if particle_panel_poll(context): + if particle_panel_poll(self, context): psys = context.particle_system return psys.settings.physics_type != 'BOIDS' and not psys.point_cache.external else: @@ -327,6 +346,7 @@ class PARTICLE_PT_rotation(ParticleButtonsPanel): psys = context.particle_system part = psys.settings + wide_ui = context.region.width > narrowui layout.enabled = particle_panel_enabled(context, psys) @@ -344,7 +364,10 @@ class PARTICLE_PT_rotation(ParticleButtonsPanel): sub.prop(part, "random_phase_factor", text="Random", slider=True) layout.row().label(text="Angular Velocity:") - layout.row().prop(part, "angular_velocity_mode", expand=True) + if wide_ui: + layout.row().prop(part, "angular_velocity_mode", expand=True) + else: + layout.row().prop(part, "angular_velocity_mode", text="") split = layout.split() sub = split.column() @@ -355,9 +378,10 @@ class PARTICLE_PT_rotation(ParticleButtonsPanel): class PARTICLE_PT_physics(ParticleButtonsPanel): bl_label = "Physics" + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): - if particle_panel_poll(context): + if particle_panel_poll(self, context): return not context.particle_system.point_cache.external else: return False @@ -367,11 +391,16 @@ class PARTICLE_PT_physics(ParticleButtonsPanel): psys = context.particle_system part = psys.settings + wide_ui = context.region.width > narrowui layout.enabled = particle_panel_enabled(context, psys) row = layout.row() - row.prop(part, "physics_type", expand=True) + if wide_ui: + row.prop(part, "physics_type", expand=True) + else: + row.prop(part, "physics_type", text="") + if part.physics_type != 'NO': row = layout.row() col = row.column(align=True) @@ -390,10 +419,53 @@ class PARTICLE_PT_physics(ParticleButtonsPanel): sub.prop(part, "drag_factor", slider=True) sub.prop(part, "damp_factor", 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, "size_deflect") sub.prop(part, "die_on_collision") - sub.prop(part, "integrator") + + elif part.physics_type == 'FLUID': + fluid = part.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, "damp_factor", 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, "size_deflect") + sub.prop(part, "die_on_collision") + + split = layout.split() + sub = split.column() + sub.label(text="Fluid Interaction:") + sub.prop(fluid, "fluid_radius", slider=True) + sub.prop(fluid, "stiffness_k") + sub.prop(fluid, "stiffness_knear") + sub.prop(fluid, "rest_density") + + sub.label(text="Viscosity:") + sub.prop(fluid, "viscosity_omega", text="Linear") + sub.prop(fluid, "viscosity_beta", text="Square") + + sub = split.column() + + sub.label(text="Springs:") + sub.prop(fluid, "spring_k", text="Force", slider=True) + sub.prop(fluid, "rest_length", slider=True) + layout.label(text="Multiple fluids interactions:") + + sub.label(text="Buoyancy:") + sub.prop(fluid, "buoyancy", slider=True) elif part.physics_type == 'KEYED': split = layout.split() @@ -454,7 +526,7 @@ class PARTICLE_PT_physics(ParticleButtonsPanel): col.prop(boids, "banking", slider=True) col.prop(boids, "height", slider=True) - if part.physics_type == 'KEYED' or part.physics_type == 'BOIDS': + if part.physics_type == 'KEYED' or part.physics_type == 'BOIDS' or part.physics_type == 'FLUID': if part.physics_type == 'BOIDS': layout.label(text="Relations:") @@ -484,7 +556,7 @@ class PARTICLE_PT_physics(ParticleButtonsPanel): col.active = psys.keyed_timing col.prop(key, "time") col.prop(key, "duration") - else: + elif part.physics_type == 'BOIDS': sub = row.row() #doesn't work yet #sub.red_alert = key.valid @@ -492,20 +564,28 @@ class PARTICLE_PT_physics(ParticleButtonsPanel): sub.prop(key, "system", text="System") layout.prop(key, "mode", expand=True) + elif part.physics_type == 'FLUID': + sub = row.row() + #doesn't work yet + #sub.red_alert = key.valid + sub.prop(key, "object", text="") + sub.prop(key, "system", text="System") class PARTICLE_PT_boidbrain(ParticleButtonsPanel): bl_label = "Boid Brain" + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): psys = context.particle_system + engine = context.scene.render.engine if psys is None: return False if psys.settings is None: return False if psys.point_cache.external: return False - return psys.settings.physics_type == 'BOIDS' + return psys.settings.physics_type == 'BOIDS' and engine in self.COMPAT_ENGINES def draw(self, context): layout = self.layout @@ -594,20 +674,23 @@ class PARTICLE_PT_boidbrain(ParticleButtonsPanel): class PARTICLE_PT_render(ParticleButtonsPanel): bl_label = "Render" + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): psys = context.particle_system + engine = context.scene.render.engine if psys is None: return False if psys.settings is None: return False - return True + return engine in self.COMPAT_ENGINES def draw(self, context): layout = self.layout psys = context.particle_system part = psys.settings + wide_ui = context.region.width > narrowui row = layout.row() row.prop(part, "material") @@ -623,7 +706,10 @@ class PARTICLE_PT_render(ParticleButtonsPanel): sub.prop(part, "died") row = layout.row() - row.prop(part, "ren_as", expand=True) + if wide_ui: + row.prop(part, "ren_as", expand=True) + else: + row.prop(part, "ren_as", text="") split = layout.split() @@ -635,12 +721,6 @@ class PARTICLE_PT_render(ParticleButtonsPanel): sub = split.column() sub.prop(part, "velocity_length") elif part.ren_as == 'PATH': - - if part.type != 'HAIR' and part.physics_type != 'KEYED' and (psys.point_cache.baked is False): - box = layout.box() - box.label(text="Baked or keyed particles needed for correct rendering.") - return - sub.prop(part, "render_strand") subsub = sub.column() subsub.active = (part.render_strand is False) @@ -716,7 +796,10 @@ class PARTICLE_PT_render(ParticleButtonsPanel): sub.label(text="Align:") row = layout.row() - row.prop(part, "billboard_align", expand=True) + if wide_ui: + row.prop(part, "billboard_align", expand=True) + else: + row.prop(part, "billboard_align", text="") row.prop(part, "billboard_lock", text="Lock") row = layout.row() row.prop(part, "billboard_object") @@ -741,9 +824,9 @@ class PARTICLE_PT_render(ParticleButtonsPanel): row.prop(psys, "billboard_split_uv") row = layout.row() row.label(text="Animate:") - row.prop(part, "billboard_animation", expand=True) + row.prop(part, "billboard_animation", text="") row.label(text="Offset:") - row.prop(part, "billboard_split_offset", expand=True) + row.prop(part, "billboard_split_offset", text="") if part.ren_as == 'HALO' or part.ren_as == 'LINE' or part.ren_as == 'BILLBOARD': row = layout.row() @@ -762,34 +845,35 @@ class PARTICLE_PT_render(ParticleButtonsPanel): class PARTICLE_PT_draw(ParticleButtonsPanel): bl_label = "Display" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def poll(self, context): psys = context.particle_system + engine = context.scene.render.engine if psys is None: return False if psys.settings is None: return False - return True + return engine in self.COMPAT_ENGINES def draw(self, context): layout = self.layout psys = context.particle_system part = psys.settings + wide_ui = context.region.width > narrowui row = layout.row() - row.prop(part, "draw_as", expand=True) + if wide_ui: + row.prop(part, "draw_as", expand=True) + else: + row.prop(part, "draw_as", text="") if part.draw_as == 'NONE' or (part.ren_as == 'NONE' and part.draw_as == 'RENDER'): return path = (part.ren_as == 'PATH' and part.draw_as == 'RENDER') or part.draw_as == 'PATH' - if path and part.type != 'HAIR' and part.physics_type != 'KEYED' and psys.point_cache.baked is False: - box = layout.box() - box.label(text="Baked or keyed particles needed for correct drawing.") - return - row = layout.row() row.prop(part, "display", slider=True) if part.draw_as != 'RENDER' or part.ren_as == 'HALO': @@ -820,14 +904,19 @@ class PARTICLE_PT_draw(ParticleButtonsPanel): class PARTICLE_PT_children(ParticleButtonsPanel): bl_label = "Children" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def draw(self, context): layout = self.layout psys = context.particle_system part = psys.settings + wide_ui = context.region.width > narrowui - layout.row().prop(part, "child_type", expand=True) + if wide_ui: + layout.row().prop(part, "child_type", expand=True) + else: + layout.row().prop(part, "child_type", text="") if part.child_type == 'NONE': return @@ -883,7 +972,10 @@ class PARTICLE_PT_children(ParticleButtonsPanel): col.label(text="hair parting controls") layout.row().label(text="Kink:") - layout.row().prop(part, "kink", expand=True) + if wide_ui: + layout.row().prop(part, "kink", expand=True) + else: + layout.row().prop(part, "kink", text="") split = layout.split() @@ -897,6 +989,7 @@ class PARTICLE_PT_children(ParticleButtonsPanel): class PARTICLE_PT_field_weights(ParticleButtonsPanel): bl_label = "Field Weights" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def draw(self, context): part = context.particle_system.settings @@ -909,6 +1002,7 @@ class PARTICLE_PT_field_weights(ParticleButtonsPanel): class PARTICLE_PT_force_fields(ParticleButtonsPanel): bl_label = "Force Field Settings" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def draw(self, context): layout = self.layout @@ -936,6 +1030,7 @@ class PARTICLE_PT_force_fields(ParticleButtonsPanel): class PARTICLE_PT_vertexgroups(ParticleButtonsPanel): bl_label = "Vertexgroups" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER'} def draw(self, context): layout = self.layout diff --git a/release/scripts/ui/properties_physics_cloth.py b/release/scripts/ui/properties_physics_cloth.py index 7a65420e172..61e1fe81812 100644 --- a/release/scripts/ui/properties_physics_cloth.py +++ b/release/scripts/ui/properties_physics_cloth.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check from properties_physics_common import point_cache_ui @@ -36,7 +36,7 @@ class CLOTH_MT_presets(bpy.types.Menu): ''' bl_label = "Cloth Presets" preset_subdir = "cloth" - preset_operator = "script.python_file_run" + preset_operator = "script.execute_preset" draw = bpy.types.Menu.draw_preset @@ -62,7 +62,6 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel): wide_ui = context.region.width > narrowui split = layout.split() - split.operator_context = 'EXEC_DEFAULT' if md: # remove modifier + settings @@ -76,9 +75,7 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel): # add modifier split.operator("object.modifier_add", text="Add").type = 'CLOTH' if wide_ui: - split.column() - - split.operator_context = 'INVOKE_DEFAULT' + split.label() if md: cloth = md.settings @@ -90,9 +87,9 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel): col = split.column() col.label(text="Presets:") - sub = col.row(align=True).split(percentage=0.75) - sub.menu("CLOTH_MT_presets", text="Presets") - sub.operator("cloth.preset_add", text="Add") + sub = col.row(align=True) + sub.menu("CLOTH_MT_presets", text=bpy.types.CLOTH_MT_presets.bl_label) + sub.operator("cloth.preset_add", text="", icon="ZOOMIN") col.label(text="Quality:") col.prop(cloth, "quality", text="Steps", slider=True) @@ -129,6 +126,12 @@ class PHYSICS_PT_cloth(PhysicButtonsPanel): col.prop(cloth, "goal_friction", text="Friction") """ + key = ob.data.shape_keys + + if key: + col.label(text="Rest Shape Key:") + col.prop_object(cloth, "rest_shape_key", key, "keys", text="") + class PHYSICS_PT_cloth_cache(PhysicButtonsPanel): bl_label = "Cloth Cache" @@ -139,7 +142,7 @@ class PHYSICS_PT_cloth_cache(PhysicButtonsPanel): def draw(self, context): md = context.cloth - point_cache_ui(self, context, md.point_cache, cloth_panel_enabled(md), 0, 0) + point_cache_ui(self, context, md.point_cache, cloth_panel_enabled(md), 'CLOTH') class PHYSICS_PT_cloth_collision(PhysicButtonsPanel): @@ -179,6 +182,8 @@ class PHYSICS_PT_cloth_collision(PhysicButtonsPanel): sub.prop(cloth, "self_collision_quality", slider=True, text="Quality") sub.prop(cloth, "self_min_distance", slider=True, text="Distance") + layout.prop(cloth, "group") + class PHYSICS_PT_cloth_stiffness(PhysicButtonsPanel): bl_label = "Cloth Stiffness Scaling" diff --git a/release/scripts/ui/properties_physics_common.py b/release/scripts/ui/properties_physics_common.py index b81fa25b1a1..be8972e4fe6 100644 --- a/release/scripts/ui/properties_physics_common.py +++ b/release/scripts/ui/properties_physics_common.py @@ -18,14 +18,18 @@ # <pep8 compliant> -narrowui = 180 +import bpy +narrowui = bpy.context.user_preferences.view.properties_width_check -def point_cache_ui(self, context, cache, enabled, particles, smoke): +#cachetype can be 'PSYS' 'HAIR' 'SMOKE' etc + + +def point_cache_ui(self, context, cache, enabled, cachetype): layout = self.layout wide_ui = context.region.width > narrowui - layout.set_context_pointer("PointCache", cache) + layout.set_context_pointer("point_cache", cache) row = layout.row() row.template_list(cache, "point_cache_list", cache, "active_point_cache_index", rows=2) @@ -34,60 +38,75 @@ def point_cache_ui(self, context, cache, enabled, particles, smoke): col.operator("ptcache.remove", icon='ZOOMOUT', text="") row = layout.row() - row.label(text="File Name:") - if particles: + if cachetype in ('PSYS', 'HAIR', 'SMOKE'): row.prop(cache, "external") if cache.external: split = layout.split(percentage=0.80) - split.prop(cache, "name", text="") + split.prop(cache, "name", text="File Name") split.prop(cache, "index", text="") - layout.label(text="File Path:") + row = layout.row() + row.label(text="File Path:") + row.prop(cache, "use_library_path", "Use Lib Path") + layout.prop(cache, "filepath", text="") layout.label(text=cache.info) else: - layout.prop(cache, "name", text="") + layout.prop(cache, "name", text="File Name") - if not particles: - row = layout.row() - row.enabled = enabled - row.prop(cache, "start_frame") - row.prop(cache, "end_frame") + split = layout.split() + col = split.column(align=True) - row = layout.row() + if cachetype != 'PSYS': + col.enabled = enabled + col.prop(cache, "frame_start") + col.prop(cache, "frame_end") + if cachetype != 'SMOKE': + col.prop(cache, "step") - if cache.baked == True: - row.operator("ptcache.free_bake", text="Free Bake") - else: - row.operator("ptcache.bake", text="Bake").bake = True + if wide_ui: + col = split.column() - sub = row.row() - sub.enabled = (cache.frames_skipped or cache.outdated) and enabled - sub.operator("ptcache.bake", "bake", False, text="Calculate to Current Frame") - - row = layout.row() - row.enabled = enabled - row.operator("ptcache.bake_from_cache", text="Current Cache to Bake") - if not smoke: - row.prop(cache, "step") - - if not smoke: - row = layout.row() - sub = row.row() + if cachetype != 'SMOKE': + sub = col.column() sub.enabled = enabled sub.prop(cache, "quick_cache") - row.prop(cache, "disk_cache") - layout.label(text=cache.info) + sub = col.column() + sub.enabled = bpy.data.file_is_saved + sub.prop(cache, "disk_cache") + col.label(text=cache.info) + + sub = col.column() + sub.prop(cache, "use_library_path", "Use Lib Path") layout.separator() - row = layout.row() - row.operator("ptcache.bake_all", text="Bake All Dynamics").bake = True - row.operator("ptcache.free_bake_all", text="Free All Bakes") - layout.operator("ptcache.bake_all", "bake", False, text="Update All Dynamics to current frame") + split = layout.split() + + col = split.column() + + if cache.baked == True: + col.operator("ptcache.free_bake", text="Free Bake") + else: + col.operator("ptcache.bake", text="Bake").bake = True + + sub = col.row() + sub.enabled = (cache.frames_skipped or cache.outdated) and enabled + sub.operator("ptcache.bake", text="Calculate To Frame").bake = False + + sub = col.column() + sub.enabled = enabled + sub.operator("ptcache.bake_from_cache", text="Current Cache to Bake") + + + if wide_ui: + col = split.column() + col.operator("ptcache.bake_all", text="Bake All Dynamics").bake = True + col.operator("ptcache.free_bake_all", text="Free All Bakes") + col.operator("ptcache.bake_all", text="Update All To Frame").bake = False def effector_weights_ui(self, context, weights): diff --git a/release/scripts/ui/properties_physics_field.py b/release/scripts/ui/properties_physics_field.py index e0be01d4c1c..57fa40e4c5d 100644 --- a/release/scripts/ui/properties_physics_field.py +++ b/release/scripts/ui/properties_physics_field.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check from properties_physics_common import basic_force_field_settings_ui @@ -72,6 +72,7 @@ class PHYSICS_PT_field(PhysicButtonsPanel): col.prop(field, "guide_free") col.prop(field, "falloff_power") col.prop(field, "guide_path_add") + col.prop(field, "use_guide_path_weight") if wide_ui: col = split.column() @@ -184,7 +185,6 @@ class PHYSICS_PT_collision(PhysicButtonsPanel): wide_ui = context.region.width > narrowui split = layout.split() - split.operator_context = 'EXEC_DEFAULT' if md: # remove modifier + settings diff --git a/release/scripts/ui/properties_physics_fluid.py b/release/scripts/ui/properties_physics_fluid.py index 9b36b98bc24..32acd88654d 100644 --- a/release/scripts/ui/properties_physics_fluid.py +++ b/release/scripts/ui/properties_physics_fluid.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class PhysicButtonsPanel(bpy.types.Panel): @@ -43,7 +43,6 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel): wide_ui = context.region.width > narrowui split = layout.split() - split.operator_context = 'EXEC_DEFAULT' if md: # remove modifier + settings @@ -67,9 +66,18 @@ class PHYSICS_PT_fluid(PhysicButtonsPanel): if fluid: if wide_ui: - layout.prop(fluid, "type") + row = layout.row() + row.prop(fluid, "type") + if fluid.type not in ('NONE', 'DOMAIN', 'PARTICLE'): + row.prop(fluid, "active", text="") else: layout.prop(fluid, "type", text="") + if fluid.type not in ('NONE', 'DOMAIN', 'PARTICLE'): + layout.prop(fluid, "active", text="") + + layout = layout.column() + if fluid.type not in ('NONE', 'DOMAIN', 'PARTICLE'): + layout.active = fluid.active if fluid.type == 'DOMAIN': layout.operator("fluid.bake", text="Bake Fluid Simulation", icon='MOD_FLUIDSIM') @@ -220,15 +228,29 @@ class PHYSICS_PT_domain_gravity(PhysicButtonsPanel): layout = self.layout fluid = context.fluid.settings + scene = context.scene wide_ui = context.region.width > narrowui split = layout.split() col = split.column() - col.label(text="Gravity:") - col.prop(fluid, "gravity", text="") - col.label(text="Real World Size:") - col.prop(fluid, "real_world_size", text="Metres") + if scene.use_gravity: + col.label(text="Using Scene Gravity", icon="SCENE_DATA") + sub = col.column() + sub.enabled = False + sub.prop(fluid, "gravity", text="") + else: + col.label(text="Gravity:") + col.prop(fluid, "gravity", text="") + + if scene.unit_settings.system != 'NONE': + col.label(text="Using Scene Size Units", icon="SCENE_DATA") + sub = col.column() + sub.enabled = False + sub.prop(fluid, "real_world_size", text="Metres") + else: + col.label(text="Real World Size:") + col.prop(fluid, "real_world_size", text="Metres") if wide_ui: col = split.column() diff --git a/release/scripts/ui/properties_physics_smoke.py b/release/scripts/ui/properties_physics_smoke.py index 1f91b761e1a..c5357c515de 100644 --- a/release/scripts/ui/properties_physics_smoke.py +++ b/release/scripts/ui/properties_physics_smoke.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check from properties_physics_common import point_cache_ui @@ -48,7 +48,6 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel): wide_ui = context.region.width > narrowui split = layout.split() - split.operator_context = 'EXEC_DEFAULT' if md: # remove modifier + settings @@ -71,8 +70,7 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel): else: layout.prop(md, "smoke_type", text="") - if md.smoke_type == 'TYPE_DOMAIN': - + if md.smoke_type == 'DOMAIN': domain = md.domain_settings split = layout.split() @@ -94,8 +92,7 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel): sub.prop(domain, "dissolve_speed", text="Time") sub.prop(domain, "dissolve_smoke_log", text="Slow") - elif md.smoke_type == 'TYPE_FLOW': - + elif md.smoke_type == 'FLOW': flow = md.flow_settings split = layout.split() @@ -115,7 +112,7 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel): col.prop(flow, "temperature") col.prop(flow, "density") - #elif md.smoke_type == 'TYPE_COLL': + #elif md.smoke_type == 'COLLISION': # layout.separator() @@ -125,7 +122,7 @@ class PHYSICS_PT_smoke_groups(PhysicButtonsPanel): def poll(self, context): md = context.smoke - return md and (md.smoke_type == 'TYPE_DOMAIN') + return md and (md.smoke_type == 'DOMAIN') def draw(self, context): layout = self.layout @@ -154,20 +151,18 @@ class PHYSICS_PT_smoke_cache(PhysicButtonsPanel): def poll(self, context): md = context.smoke - return md and (md.smoke_type == 'TYPE_DOMAIN') + return md and (md.smoke_type == 'DOMAIN') def draw(self, context): layout = self.layout - domain = context.smoke.domain_settings - - layout.label(text="Compression:") - layout.prop(domain, "smoke_cache_comp", expand=True) - md = context.smoke.domain_settings cache = md.point_cache_low - point_cache_ui(self, context, cache, (cache.baked is False), 0, 1) + layout.label(text="Compression:") + layout.prop(md, "smoke_cache_comp", expand=True) + + point_cache_ui(self, context, cache, (cache.baked is False), 'SMOKE') class PHYSICS_PT_smoke_highres(PhysicButtonsPanel): @@ -176,12 +171,12 @@ class PHYSICS_PT_smoke_highres(PhysicButtonsPanel): def poll(self, context): md = context.smoke - return md and (md.smoke_type == 'TYPE_DOMAIN') + return md and (md.smoke_type == 'DOMAIN') def draw_header(self, context): - high = context.smoke.domain_settings + md = context.smoke.domain_settings - self.layout.prop(high, "highres", text="") + self.layout.prop(md, "highres", text="") def draw(self, context): layout = self.layout @@ -189,6 +184,8 @@ class PHYSICS_PT_smoke_highres(PhysicButtonsPanel): md = context.smoke.domain_settings wide_ui = context.region.width > narrowui + layout.active = md.highres + split = layout.split() col = split.column() @@ -209,21 +206,18 @@ class PHYSICS_PT_smoke_cache_highres(PhysicButtonsPanel): def poll(self, context): md = context.smoke - return md and (md.smoke_type == 'TYPE_DOMAIN') and md.domain_settings.highres + return md and (md.smoke_type == 'DOMAIN') and md.domain_settings.highres def draw(self, context): layout = self.layout - domain = context.smoke.domain_settings - - layout.label(text="Compression:") - layout.prop(domain, "smoke_cache_high_comp", expand=True) - - md = context.smoke.domain_settings cache = md.point_cache_high - point_cache_ui(self, context, cache, (cache.baked is False), 0, 1) + layout.label(text="Compression:") + layout.prop(md, "smoke_cache_high_comp", expand=True) + + point_cache_ui(self, context, cache, (cache.baked is False), 'SMOKE') class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel): @@ -232,7 +226,7 @@ class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel): def poll(self, context): smoke = context.smoke - return (smoke and smoke.smoke_type == 'TYPE_DOMAIN') + return (smoke and smoke.smoke_type == 'DOMAIN') def draw(self, context): domain = context.smoke.domain_settings diff --git a/release/scripts/ui/properties_physics_softbody.py b/release/scripts/ui/properties_physics_softbody.py index 8a59e087dc3..60149cf533b 100644 --- a/release/scripts/ui/properties_physics_softbody.py +++ b/release/scripts/ui/properties_physics_softbody.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check from properties_physics_common import point_cache_ui @@ -38,7 +38,9 @@ class PhysicButtonsPanel(bpy.types.Panel): def poll(self, context): ob = context.object rd = context.scene.render - return (ob and ob.type == 'MESH') and (not rd.use_game_engine) +# return (ob and ob.type == 'MESH') and (not rd.use_game_engine) +# i really hate touching things i do not understand completely .. but i think this should read (bjornmose) + return (ob and (ob.type == 'MESH' or ob.type == 'LATTICE'or ob.type == 'CURVE')) and (not rd.use_game_engine) class PHYSICS_PT_softbody(PhysicButtonsPanel): @@ -52,7 +54,6 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel): wide_ui = context.region.width > narrowui split = layout.split() - split.operator_context = 'EXEC_DEFAULT' if md: # remove modifier + settings @@ -96,7 +97,7 @@ class PHYSICS_PT_softbody_cache(PhysicButtonsPanel): def draw(self, context): md = context.soft_body - point_cache_ui(self, context, md.point_cache, softbody_panel_enabled(md), 0, 0) + point_cache_ui(self, context, md.point_cache, softbody_panel_enabled(md), 'SOFTBODY') class PHYSICS_PT_softbody_goal(PhysicButtonsPanel): @@ -185,10 +186,13 @@ class PHYSICS_PT_softbody_edge(PhysicButtonsPanel): sub.active = softbody.stiff_quads sub.prop(softbody, "shear") - col.prop(softbody, "new_aero", text="Aero") - sub = col.column() - sub.enabled = softbody.new_aero - sub.prop(softbody, "aero", text="Factor") + col.label(text="Aerodynamics:") + col.row().prop(softbody, "aerodynamics_type", expand=True) + col.prop(softbody, "aero", text="Factor") + + #sub = col.column() + #sub.enabled = softbody.aero > 0 + col.label(text="Collision:") col.prop(softbody, "edge_collision", text="Edge") @@ -196,7 +200,7 @@ class PHYSICS_PT_softbody_edge(PhysicButtonsPanel): class PHYSICS_PT_softbody_collision(PhysicButtonsPanel): - bl_label = "Soft Body Collision" + bl_label = "Soft Body Self Collision" bl_default_closed = True def poll(self, context): @@ -217,7 +221,7 @@ class PHYSICS_PT_softbody_collision(PhysicButtonsPanel): layout.active = softbody.self_collision and softbody_panel_enabled(md) - layout.label(text="Collision Type:") + layout.label(text="Collision Ball Size Calculation:") if wide_ui: layout.prop(softbody, "collision_type", expand=True) else: diff --git a/release/scripts/ui/properties_render.py b/release/scripts/ui/properties_render.py index 8bf3318c3e2..829416b86d3 100644 --- a/release/scripts/ui/properties_render.py +++ b/release/scripts/ui/properties_render.py @@ -19,12 +19,19 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class RENDER_MT_presets(bpy.types.Menu): bl_label = "Render Presets" preset_subdir = "render" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class RENDER_MT_ffmpeg_presets(bpy.types.Menu): + bl_label = "FFMPEG Presets" + preset_subdir = "ffmpeg" preset_operator = "script.python_file_run" draw = bpy.types.Menu.draw_preset @@ -53,11 +60,11 @@ class RENDER_PT_render(RenderButtonsPanel): split = layout.split() col = split.column() - col.operator("screen.render", text="Image", icon='RENDER_STILL') + col.operator("render.render", text="Image", icon='RENDER_STILL') if wide_ui: col = split.column() - col.operator("screen.render", text="Animation", icon='RENDER_ANIMATION').animation = True + col.operator("render.render", text="Animation", icon='RENDER_ANIMATION').animation = True layout.prop(rd, "display_mode", text="Display") @@ -89,13 +96,16 @@ class RENDER_PT_layers(RenderButtonsPanel): split = layout.split() col = split.column() - col.prop(scene, "visible_layers", text="Scene") + col.prop(scene, "layers", text="Scene") + col.label(text="") + col.prop(rl, "light_override", text="Light") + col.prop(rl, "material_override", text="Material") if wide_ui: col = split.column() col.prop(rl, "visible_layers", text="Layer") + col.label(text="Mask Layers:") + col.prop(rl, "zmask_layers", text="") - layout.prop(rl, "light_override", text="Light") - layout.prop(rl, "material_override", text="Material") layout.separator() layout.label(text="Include:") @@ -119,11 +129,6 @@ class RENDER_PT_layers(RenderButtonsPanel): col.prop(rl, "edge") col.prop(rl, "strand") - if rl.zmask: - split = layout.split() - split.label(text="Zmask Layers:") - split.column().prop(rl, "zmask_layers", text="") - layout.separator() split = layout.split() @@ -145,28 +150,28 @@ class RENDER_PT_layers(RenderButtonsPanel): col.prop(rl, "pass_diffuse") row = col.row() row.prop(rl, "pass_specular") - row.prop(rl, "pass_specular_exclude", text="", icon='X') + row.prop(rl, "pass_specular_exclude", text="") row = col.row() row.prop(rl, "pass_shadow") - row.prop(rl, "pass_shadow_exclude", text="", icon='X') + row.prop(rl, "pass_shadow_exclude", text="") row = col.row() row.prop(rl, "pass_emit") - row.prop(rl, "pass_emit_exclude", text="", icon='X') + row.prop(rl, "pass_emit_exclude", text="") row = col.row() row.prop(rl, "pass_ao") - row.prop(rl, "pass_ao_exclude", text="", icon='X') + row.prop(rl, "pass_ao_exclude", text="") row = col.row() row.prop(rl, "pass_environment") - row.prop(rl, "pass_environment_exclude", text="", icon='X') + row.prop(rl, "pass_environment_exclude", text="") row = col.row() row.prop(rl, "pass_indirect") - row.prop(rl, "pass_indirect_exclude", text="", icon='X') + row.prop(rl, "pass_indirect_exclude", text="") row = col.row() row.prop(rl, "pass_reflection") - row.prop(rl, "pass_reflection_exclude", text="", icon='X') + row.prop(rl, "pass_reflection_exclude", text="") row = col.row() row.prop(rl, "pass_refraction") - row.prop(rl, "pass_refraction_exclude", text="", icon='X') + row.prop(rl, "pass_refraction_exclude", text="") class RENDER_PT_shading(RenderButtonsPanel): @@ -182,14 +187,14 @@ class RENDER_PT_shading(RenderButtonsPanel): split = layout.split() col = split.column() - col.prop(rd, "render_textures", text="Textures") - col.prop(rd, "render_shadows", text="Shadows") - col.prop(rd, "render_sss", text="Subsurface Scattering") - col.prop(rd, "render_envmaps", text="Environment Map") + col.prop(rd, "use_textures", text="Textures") + col.prop(rd, "use_shadows", text="Shadows") + col.prop(rd, "use_sss", text="Subsurface Scattering") + col.prop(rd, "use_envmaps", text="Environment Map") if wide_ui: col = split.column() - col.prop(rd, "render_raytracing", text="Ray Tracing") + col.prop(rd, "use_raytracing", text="Ray Tracing") col.prop(rd, "color_management") col.prop(rd, "alpha_mode", text="Alpha") @@ -211,7 +216,7 @@ class RENDER_PT_performance(RenderButtonsPanel): col.label(text="Threads:") col.row().prop(rd, "threads_mode", expand=True) sub = col.column() - sub.enabled = rd.threads_mode == 'THREADS_FIXED' + sub.enabled = rd.threads_mode == 'FIXED' sub.prop(rd, "threads") sub = col.column(align=True) sub.label(text="Tiles:") @@ -228,7 +233,7 @@ class RENDER_PT_performance(RenderButtonsPanel): sub.active = rd.use_compositing sub.prop(rd, "free_image_textures") sub = col.column() - sub.active = rd.render_raytracing + sub.active = rd.use_raytracing sub.label(text="Acceleration structure:") sub.prop(rd, "raytrace_structure", text="") if rd.raytrace_structure == 'OCTREE': @@ -305,11 +310,20 @@ class RENDER_PT_output(RenderButtonsPanel): col.prop(rd, "use_overwrite") col.prop(rd, "use_placeholder") - if rd.file_format in ('AVIJPEG', 'JPEG'): + if rd.file_format in ('AVI_JPEG', 'JPEG'): split = layout.split() - split.prop(rd, "quality", slider=True) + split.prop(rd, "file_quality", slider=True) + + elif rd.file_format == 'MULTILAYER': + split = layout.split() + + col = split.column() + col.label(text="Codec:") + col.prop(rd, "exr_codec", text="") + if wide_ui: + col = split.column() - elif rd.file_format == 'OPENEXR': + elif rd.file_format == 'OPEN_EXR': split = layout.split() col = split.column() @@ -355,14 +369,38 @@ class RENDER_PT_output(RenderButtonsPanel): elif rd.file_format == 'QUICKTIME_CARBON': split = layout.split() - split.operator("scene.render_set_quicktime_codec") + split.operator("scene.render_data_set_quicktime_codec") elif rd.file_format == 'QUICKTIME_QTKIT': split = layout.split() col = split.column() - col.prop(rd, "quicktime_codec_type") + col.prop(rd, "quicktime_codec_type", text="Video Codec") col.prop(rd, "quicktime_codec_spatial_quality", text="Quality") + # Audio + 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="") + if wide_ui: + col = split.column() + col.prop(rd, "quicktime_audio_samplerate", text="") + + split = layout.split() + col = split.column() + if rd.quicktime_audiocodec_type == 'AAC': + col.prop(rd, "quicktime_audio_bitrate") + if wide_ui: + subsplit = split.split() + col = subsplit.column() + if rd.quicktime_audiocodec_type == 'AAC': + col.prop(rd, "quicktime_audio_codec_isvbr") + if wide_ui: + col = subsplit.column() + col.prop(rd, "quicktime_audio_resampling_hq") + class RENDER_PT_encoding(RenderButtonsPanel): bl_label = "Encoding" @@ -379,6 +417,8 @@ class RENDER_PT_encoding(RenderButtonsPanel): rd = context.scene.render wide_ui = context.region.width > narrowui + layout.menu("RENDER_MT_ffmpeg_presets", text="Presets") + split = layout.split() col = split.column() @@ -441,14 +481,14 @@ class RENDER_PT_antialiasing(RenderButtonsPanel): def draw_header(self, context): rd = context.scene.render - self.layout.prop(rd, "antialiasing", text="") + self.layout.prop(rd, "render_antialiasing", text="") def draw(self, context): layout = self.layout rd = context.scene.render wide_ui = context.region.width > narrowui - layout.active = rd.antialiasing + layout.active = rd.render_antialiasing split = layout.split() @@ -482,6 +522,7 @@ class RENDER_PT_motion_blur(RenderButtonsPanel): row = layout.row() row.prop(rd, "motion_blur_samples") + row.prop(rd, "motion_blur_shutter") class RENDER_PT_dimensions(RenderButtonsPanel): @@ -495,10 +536,9 @@ class RENDER_PT_dimensions(RenderButtonsPanel): rd = scene.render wide_ui = context.region.width > narrowui - row = layout.row().split() - sub = row.row(align=True).split(percentage=0.75) - sub.menu("RENDER_MT_presets", text="Presets") - sub.operator("render.preset_add", text="Add") + row = layout.row(align=True) + row.menu("RENDER_MT_presets", text=bpy.types.RENDER_MT_presets.bl_label) + row.operator("render.preset_add", text="", icon="ZOOMIN") split = layout.split() @@ -523,8 +563,8 @@ class RENDER_PT_dimensions(RenderButtonsPanel): col = split.column() sub = col.column(align=True) sub.label(text="Frame Range:") - sub.prop(scene, "start_frame", text="Start") - sub.prop(scene, "end_frame", text="End") + sub.prop(scene, "frame_start", text="Start") + sub.prop(scene, "frame_end", text="End") sub.prop(scene, "frame_step", text="Step") sub.label(text="Frame Rate:") @@ -627,6 +667,7 @@ class RENDER_PT_bake(RenderButtonsPanel): classes = [ RENDER_MT_presets, + RENDER_MT_ffmpeg_presets, RENDER_PT_render, RENDER_PT_layers, RENDER_PT_dimensions, diff --git a/release/scripts/ui/properties_scene.py b/release/scripts/ui/properties_scene.py index 0209140ef4e..06ad1eda835 100644 --- a/release/scripts/ui/properties_scene.py +++ b/release/scripts/ui/properties_scene.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class SceneButtonsPanel(bpy.types.Panel): @@ -96,17 +96,16 @@ class SCENE_PT_keying_sets(SceneButtonsPanel): col.operator("anim.keying_set_remove", icon='ZOOMOUT', text="") ks = scene.active_keying_set - if ks: + if ks and ks.absolute: row = layout.row() col = row.column() col.prop(ks, "name") - col.prop(ks, "absolute") subcol = col.column() subcol.operator_context = 'INVOKE_DEFAULT' op = subcol.operator("anim.keying_set_export", text="Export to File") - op.path = "keyingset.py" + op.filepath = "keyingset.py" if wide_ui: col = row.column() @@ -120,7 +119,7 @@ class SCENE_PT_keying_set_paths(SceneButtonsPanel): bl_label = "Active Keying Set" def poll(self, context): - return (context.scene.active_keying_set is not None) + return (context.scene.active_keying_set and context.scene.active_keying_set.absolute) def draw(self, context): layout = self.layout @@ -164,6 +163,11 @@ class SCENE_PT_keying_set_paths(SceneButtonsPanel): if ksp.grouping == 'NAMED': col.prop(ksp, "group") + col.label(text="Keyframing Settings:") + col.prop(ksp, "insertkey_needed", text="Needed") + col.prop(ksp, "insertkey_visual", text="Visual") + col.prop(ksp, "insertkey_xyz_to_rgb", text="XYZ to RGB") + class SCENE_PT_physics(SceneButtonsPanel): bl_label = "Gravity" @@ -225,18 +229,16 @@ class ANIM_OT_keying_set_export(bpy.types.Operator): bl_idname = "anim.keying_set_export" bl_label = "Export Keying Set..." - path = bpy.props.StringProperty(name="File Path", description="File path to write file to.") - filename = bpy.props.StringProperty(name="File Name", description="Name of the file.") - directory = bpy.props.StringProperty(name="Directory", description="Directory of the file.") + filepath = bpy.props.StringProperty(name="File Path", description="Filepath to write file to.") filter_folder = bpy.props.BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) filter_text = bpy.props.BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'}) filter_python = bpy.props.BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) def execute(self, context): - if not self.properties.path: - raise Exception("File path not set.") + if not self.properties.filepath: + raise Exception("Filepath not set.") - f = open(self.properties.path, "w") + f = open(self.properties.filepath, "w") if not f: raise Exception("Could not open file.") @@ -295,9 +297,9 @@ class ANIM_OT_keying_set_export(bpy.types.Operator): # write paths f.write("# Path Definitions\n") for ksp in ks.paths: - f.write("ksp = ks.add_destination(") + f.write("ksp = ks.paths.add(") - # id-block + RNA-path + # id-block + data_path if ksp.id: # find the relevant shorthand from the cache id_bpy_path = id_to_paths_cache[ksp.id][0] @@ -306,8 +308,10 @@ class ANIM_OT_keying_set_export(bpy.types.Operator): f.write("%s, '%s'" % (id_bpy_path, ksp.data_path)) # array index settings (if applicable) - if ksp.entire_array is False: - f.write(", entire_array=False, array_index=%d" % ksp.array_index) + if ksp.entire_array: + f.write(", index=-1") + else: + f.write(", index=%d" % ksp.array_index) # grouping settings (if applicable) # NOTE: the current default is KEYINGSET, but if this changes, change this code too diff --git a/release/scripts/ui/properties_texture.py b/release/scripts/ui/properties_texture.py index 8794b19bd4d..2bfbe188c9e 100644 --- a/release/scripts/ui/properties_texture.py +++ b/release/scripts/ui/properties_texture.py @@ -20,24 +20,38 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check -def active_node_mat(mat): - if mat: - mat_node = mat.active_node_material - if mat_node: - return mat_node - else: - return mat +class TEXTURE_MT_specials(bpy.types.Menu): + bl_label = "Texture Specials" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def draw(self, context): + layout = self.layout + + layout.operator("texture.slot_copy", icon='COPYDOWN') + layout.operator("texture.slot_paste", icon='PASTEDOWN') + + +class TEXTURE_MT_envmap_specials(bpy.types.Menu): + bl_label = "Environment Map Specials" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def draw(self, context): + layout = self.layout - return None + layout.operator("texture.envmap_save", icon='IMAGEFILE') + layout.operator("texture.envmap_clear", icon='FILE_REFRESH') + layout.operator("texture.envmap_clear_all", icon='FILE_REFRESH') + +from properties_material import active_node_mat def context_tex_datablock(context): - idblock = active_node_mat(context.material) + idblock = context.material if idblock: - return idblock + return active_node_mat(idblock) idblock = context.lamp if idblock: @@ -58,18 +72,21 @@ class TextureButtonsPanel(bpy.types.Panel): def poll(self, context): tex = context.texture - return (tex and (tex.type != 'NONE' or tex.use_nodes)) + if not tex: + return False + engine = context.scene.render.engine + return (tex.type != 'NONE' or tex.use_nodes) and (engine in self.COMPAT_ENGINES) class TEXTURE_PT_preview(TextureButtonsPanel): bl_label = "Preview" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout tex = context.texture - slot = context.texture_slot - + slot = getattr(context, "texture_slot", None) idblock = context_tex_datablock(context) if idblock: @@ -81,9 +98,14 @@ class TEXTURE_PT_preview(TextureButtonsPanel): class TEXTURE_PT_context_texture(TextureButtonsPanel): bl_label = "" bl_show_header = False + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): - return (context.material or context.world or context.lamp or context.brush or context.texture) + 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) + and (engine in self.COMPAT_ENGINES)) def draw(self, context): layout = self.layout @@ -103,6 +125,7 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel): col = row.column(align=True) col.operator("texture.slot_move", text="", icon='TRIA_UP').type = 'UP' col.operator("texture.slot_move", text="", icon='TRIA_DOWN').type = 'DOWN' + col.menu("TEXTURE_MT_specials", icon='DOWNARROW_HLT', text="") if wide_ui: split = layout.split(percentage=0.65) @@ -145,14 +168,17 @@ class TEXTURE_PT_context_texture(TextureButtonsPanel): class TEXTURE_PT_custom_props(TextureButtonsPanel, PropertyPanel): _context_path = "texture" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): # use alternate poll since NONE texture type is ok - return context.texture + engine = context.scene.render.engine + return context.texture and (engine in self.COMPAT_ENGINES) class TEXTURE_PT_colors(TextureButtonsPanel): bl_label = "Colors" bl_default_closed = True + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -178,19 +204,36 @@ class TEXTURE_PT_colors(TextureButtonsPanel): col.label(text="Adjust:") col.prop(tex, "brightness") col.prop(tex, "contrast") + col.prop(tex, "saturation") # Texture Slot Panels # class TextureSlotPanel(TextureButtonsPanel): + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): - return (context.texture_slot and - TextureButtonsPanel.poll(self, context)) + if not hasattr(context, "texture_slot"): + return False + + engine = context.scene.render.engine + return TextureButtonsPanel.poll(self, context) and (engine in self.COMPAT_ENGINES) class TEXTURE_PT_mapping(TextureSlotPanel): bl_label = "Mapping" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def poll(self, context): + idblock = context_tex_datablock(context) + if type(idblock) == bpy.types.Brush and not context.sculpt_object: + return False + + if not getattr(context, "texture_slot", None): + return False + + engine = context.scene.render.engine + return (engine in self.COMPAT_ENGINES) def draw(self, context): layout = self.layout @@ -231,15 +274,13 @@ class TEXTURE_PT_mapping(TextureSlotPanel): split.prop(tex, "object", text="") if type(idblock) == bpy.types.Brush: - layout.prop(tex, "map_mode", expand=True) - - row = layout.row() - row.active = tex.map_mode in ('FIXED', 'TILED') - row.prop(tex, "angle") + if context.sculpt_object: + layout.label(text="Brush Mapping:") + layout.prop(tex, "map_mode", expand=True) - row = layout.row() - row.active = tex.map_mode in ('TILED', '3D') - row.column().prop(tex, "size") + row = layout.row() + row.active = tex.map_mode in ('FIXED', 'TILED') + row.prop(tex, "angle") else: if type(idblock) == bpy.types.Material: split = layout.split(percentage=0.3) @@ -263,26 +304,33 @@ class TEXTURE_PT_mapping(TextureSlotPanel): row.prop(tex, "y_mapping", text="") row.prop(tex, "z_mapping", text="") - # any non brush - split = layout.split() + split = layout.split() + + col = split.column() + col.prop(tex, "offset") + if wide_ui: col = split.column() - col.prop(tex, "offset") + else: + col.separator() - if wide_ui: - col = split.column() - col.prop(tex, "size") + col.prop(tex, "size") class TEXTURE_PT_influence(TextureSlotPanel): bl_label = "Influence" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): idblock = context_tex_datablock(context) if type(idblock) == bpy.types.Brush: return False - return context.texture_slot + if not getattr(context, "texture_slot", None): + return False + + engine = context.scene.render.engine + return (engine in self.COMPAT_ENGINES) def draw(self, context): @@ -326,7 +374,8 @@ class TEXTURE_PT_influence(TextureSlotPanel): factor_but(col, tex.map_raymir, "map_raymir", "raymir_factor", "Ray Mirror") col.label(text="Geometry:") - factor_but(col, tex.map_normal, "map_normal", "normal_factor", "Normal") + # XXX replace 'or' when displacement is fixed to not rely on normal influence value. + factor_but(col, (tex.map_normal or tex.map_displacement), "map_normal", "normal_factor", "Normal") factor_but(col, tex.map_warp, "map_warp", "warp_factor", "Warp") factor_but(col, tex.map_displacement, "map_displacement", "displacement_factor", "Displace") @@ -345,7 +394,7 @@ class TEXTURE_PT_influence(TextureSlotPanel): if wide_ui: col = split.column() col.label(text=" ") - factor_but(col, tex.map_alpha, "map_coloremission", "coloremission_factor", "Emission Color") + factor_but(col, tex.map_coloremission, "map_coloremission", "coloremission_factor", "Emission Color") factor_but(col, tex.map_colortransmission, "map_colortransmission", "colortransmission_factor", "Transmission Color") factor_but(col, tex.map_colorreflection, "map_colorreflection", "colorreflection_factor", "Reflection Color") @@ -394,15 +443,18 @@ class TEXTURE_PT_influence(TextureSlotPanel): class TextureTypePanel(TextureButtonsPanel): + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): tex = context.texture - return (tex and tex.type == self.tex_type and not tex.use_nodes) + engine = context.scene.render.engine + return ((tex and tex.type == self.tex_type and not tex.use_nodes) and (engine in self.COMPAT_ENGINES)) class TEXTURE_PT_clouds(TextureTypePanel): bl_label = "Clouds" tex_type = 'CLOUDS' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -432,6 +484,7 @@ class TEXTURE_PT_clouds(TextureTypePanel): class TEXTURE_PT_wood(TextureTypePanel): bl_label = "Wood" tex_type = 'WOOD' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -468,6 +521,7 @@ class TEXTURE_PT_wood(TextureTypePanel): class TEXTURE_PT_marble(TextureTypePanel): bl_label = "Marble" tex_type = 'MARBLE' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -499,6 +553,7 @@ class TEXTURE_PT_marble(TextureTypePanel): class TEXTURE_PT_magic(TextureTypePanel): bl_label = "Magic" tex_type = 'MAGIC' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -519,6 +574,7 @@ class TEXTURE_PT_magic(TextureTypePanel): class TEXTURE_PT_blend(TextureTypePanel): bl_label = "Blend" tex_type = 'BLEND' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -540,6 +596,7 @@ class TEXTURE_PT_blend(TextureTypePanel): class TEXTURE_PT_stucci(TextureTypePanel): bl_label = "Stucci" tex_type = 'STUCCI' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -568,6 +625,7 @@ class TEXTURE_PT_stucci(TextureTypePanel): class TEXTURE_PT_image(TextureTypePanel): bl_label = "Image" tex_type = 'IMAGE' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -577,10 +635,24 @@ class TEXTURE_PT_image(TextureTypePanel): layout.template_image(tex, "image", tex.image_user) +def texture_filter_common(tex, layout): + layout.label(text="Filter:") + layout.prop(tex, "filter", text="") + if tex.mipmap and tex.filter in ('AREA', 'EWA', 'FELINE'): + if tex.filter == 'FELINE': + layout.prop(tex, "filter_probes", text="Probes") + else: + layout.prop(tex, "filter_eccentricity", text="Eccentricity") + + layout.prop(tex, "filter_size") + layout.prop(tex, "filter_size_minimum") + + class TEXTURE_PT_image_sampling(TextureTypePanel): bl_label = "Image Sampling" bl_default_closed = True tex_type = 'IMAGE' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -608,28 +680,20 @@ class TEXTURE_PT_image_sampling(TextureTypePanel): row.active = tex.normal_map row.prop(tex, "normal_space", text="") - col.label(text="Filter:") - col.prop(tex, "filter", text="") - col.prop(tex, "filter_size") - col.prop(tex, "filter_size_minimum") col.prop(tex, "mipmap") - row = col.row() row.active = tex.mipmap row.prop(tex, "mipmap_gauss") - col.prop(tex, "interpolation") - if tex.mipmap and tex.filter != 'DEFAULT': - if tex.filter == 'FELINE': - col.prop(tex, "filter_probes", text="Probes") - else: - col.prop(tex, "filter_eccentricity", text="Eccentricity") + + texture_filter_common(tex, col) class TEXTURE_PT_image_mapping(TextureTypePanel): bl_label = "Image Mapping" bl_default_closed = True tex_type = 'IMAGE' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -687,6 +751,7 @@ class TEXTURE_PT_image_mapping(TextureTypePanel): class TEXTURE_PT_plugin(TextureTypePanel): bl_label = "Plugin" tex_type = 'PLUGIN' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -699,18 +764,62 @@ class TEXTURE_PT_plugin(TextureTypePanel): class TEXTURE_PT_envmap(TextureTypePanel): bl_label = "Environment Map" tex_type = 'ENVIRONMENT_MAP' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout - # tex = context.texture + tex = context.texture + env = tex.environment_map - layout.label(text="Nothing yet") + wide_ui = context.region.width > narrowui + + row = layout.row() + row.prop(env, "source", expand=True) + row.menu("TEXTURE_MT_envmap_specials", icon='DOWNARROW_HLT', text="") + + if env.source == 'IMAGE_FILE': + layout.template_ID(tex, "image", open="image.open") + layout.template_image(tex, "image", tex.image_user, compact=True) + else: + layout.prop(env, "mapping") + if env.mapping == 'PLANE': + layout.prop(env, "zoom") + layout.prop(env, "viewpoint_object") + + split = layout.split() + + col = split.column() + col.prop(env, "ignore_layers") + col.prop(env, "resolution") + col.prop(env, "depth") + + if wide_ui: + col = split.column(align=True) + + col.label(text="Clipping:") + col.prop(env, "clip_start", text="Start") + col.prop(env, "clip_end", text="End") + + +class TEXTURE_PT_envmap_sampling(TextureTypePanel): + bl_label = "Environment Map Sampling" + bl_default_closed = True + tex_type = 'ENVIRONMENT_MAP' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def draw(self, context): + layout = self.layout + + tex = context.texture + + texture_filter_common(tex, layout) class TEXTURE_PT_musgrave(TextureTypePanel): bl_label = "Musgrave" tex_type = 'MUSGRAVE' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -758,6 +867,7 @@ class TEXTURE_PT_musgrave(TextureTypePanel): class TEXTURE_PT_voronoi(TextureTypePanel): bl_label = "Voronoi" tex_type = 'VORONOI' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -801,6 +911,7 @@ class TEXTURE_PT_voronoi(TextureTypePanel): class TEXTURE_PT_distortednoise(TextureTypePanel): bl_label = "Distorted Noise" tex_type = 'DISTORTED_NOISE' + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def draw(self, context): layout = self.layout @@ -828,10 +939,12 @@ class TEXTURE_PT_distortednoise(TextureTypePanel): class TEXTURE_PT_voxeldata(TextureButtonsPanel): bl_label = "Voxel Data" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): tex = context.texture - return (tex and tex.type == 'VOXEL_DATA') + engine = context.scene.render.engine + return (tex and tex.type == 'VOXEL_DATA' and (engine in self.COMPAT_ENGINES)) def draw(self, context): layout = self.layout @@ -848,7 +961,9 @@ class TEXTURE_PT_voxeldata(TextureButtonsPanel): layout.prop(vd, "domain_object") layout.prop(vd, "smoke_data_type") elif vd.file_format == 'IMAGE_SEQUENCE': - layout.template_image(tex, "image", tex.image_user) + layout.template_ID(tex, "image", open="image.open") + layout.template_image(tex, "image", tex.image_user, compact=True) + #layout.prop(vd, "frames") layout.prop(vd, "still") row = layout.row() @@ -862,10 +977,12 @@ class TEXTURE_PT_voxeldata(TextureButtonsPanel): class TEXTURE_PT_pointdensity(TextureButtonsPanel): bl_label = "Point Density" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): tex = context.texture - return (tex and tex.type == 'POINT_DENSITY') + engine = context.scene.render.engine + return (tex and tex.type == 'POINT_DENSITY' and (engine in self.COMPAT_ENGINES)) def draw(self, context): layout = self.layout @@ -920,10 +1037,12 @@ class TEXTURE_PT_pointdensity(TextureButtonsPanel): class TEXTURE_PT_pointdensity_turbulence(TextureButtonsPanel): bl_label = "Turbulence" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} def poll(self, context): tex = context.texture - return (tex and tex.type == 'POINT_DENSITY') + engine = context.scene.render.engine + return (tex and tex.type == 'POINT_DENSITY' and (engine in self.COMPAT_ENGINES)) def draw_header(self, context): layout = self.layout @@ -958,6 +1077,9 @@ class TEXTURE_PT_pointdensity_turbulence(TextureButtonsPanel): classes = [ + TEXTURE_MT_specials, + TEXTURE_MT_envmap_specials, + TEXTURE_PT_context_texture, TEXTURE_PT_preview, @@ -972,6 +1094,7 @@ classes = [ TEXTURE_PT_image_mapping, TEXTURE_PT_plugin, TEXTURE_PT_envmap, + TEXTURE_PT_envmap_sampling, TEXTURE_PT_musgrave, TEXTURE_PT_voronoi, TEXTURE_PT_distortednoise, diff --git a/release/scripts/ui/properties_world.py b/release/scripts/ui/properties_world.py index 1ee47e942a0..e63d513a4b5 100644 --- a/release/scripts/ui/properties_world.py +++ b/release/scripts/ui/properties_world.py @@ -20,7 +20,7 @@ import bpy from rna_prop_ui import PropertyPanel -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class WorldButtonsPanel(bpy.types.Panel): @@ -111,14 +111,14 @@ class WORLD_PT_mist(WorldButtonsPanel): def draw_header(self, context): world = context.world - self.layout.prop(world.mist, "enabled", text="") + self.layout.prop(world.mist, "use_mist", text="") def draw(self, context): layout = self.layout wide_ui = context.region.width > narrowui world = context.world - layout.active = world.mist.enabled + layout.active = world.mist.use_mist split = layout.split() @@ -142,14 +142,14 @@ class WORLD_PT_stars(WorldButtonsPanel): def draw_header(self, context): world = context.world - self.layout.prop(world.stars, "enabled", text="") + self.layout.prop(world.stars, "use_stars", text="") def draw(self, context): layout = self.layout wide_ui = context.region.width > narrowui world = context.world - layout.active = world.stars.enabled + layout.active = world.stars.use_stars split = layout.split() diff --git a/release/scripts/ui/space_buttons.py b/release/scripts/ui/space_buttons.py index 019f751449f..9fc30a8189b 100644 --- a/release/scripts/ui/space_buttons.py +++ b/release/scripts/ui/space_buttons.py @@ -38,7 +38,7 @@ class Buttons_HT_header(bpy.types.Header): row = layout.row() row.prop(so, "buttons_context", expand=True, text="") - row.prop(scene, "current_frame") + row.prop(scene, "frame_current") class Buttons_MT_view(bpy.types.Menu): diff --git a/release/scripts/ui/space_dopesheet.py b/release/scripts/ui/space_dopesheet.py index 1dc8ffd1af9..41917acd997 100644 --- a/release/scripts/ui/space_dopesheet.py +++ b/release/scripts/ui/space_dopesheet.py @@ -77,6 +77,7 @@ class DOPESHEET_MT_view(bpy.types.Menu): layout.prop(st, "show_cframe_indicator") layout.prop(st, "show_sliders") layout.prop(st, "automerge_keyframes") + layout.prop(st, "use_marker_sync") if st.show_seconds: layout.operator("anim.time_toggle", text="Show Frames") @@ -123,6 +124,9 @@ class DOPESHEET_MT_select(bpy.types.Menu): layout.operator("action.select_more") layout.operator("action.select_less") + layout.separator() + layout.operator("action.select_linked") + class DOPESHEET_MT_channel(bpy.types.Menu): bl_label = "Channel" @@ -154,8 +158,8 @@ class DOPESHEET_MT_key(bpy.types.Menu): layout.column() layout.menu("DOPESHEET_MT_key_transform", text="Transform") - layout.operator_menu_enum("action.snap", property="type", text="Snap") - layout.operator_menu_enum("action.mirror", property="type", text="Mirror") + layout.operator_menu_enum("action.snap", "type", text="Snap") + layout.operator_menu_enum("action.mirror", "type", text="Mirror") layout.separator() layout.operator("action.keyframe_insert") @@ -165,10 +169,10 @@ class DOPESHEET_MT_key(bpy.types.Menu): layout.operator("action.delete") layout.separator() - layout.operator_menu_enum("action.keyframe_type", property="type", text="Keyframe Type") - layout.operator_menu_enum("action.handle_type", property="type", text="Handle Type") - layout.operator_menu_enum("action.interpolation_type", property="type", text="Interpolation Mode") - layout.operator_menu_enum("action.extrapolation_type", property="type", text="Extrapolation Mode") + layout.operator_menu_enum("action.keyframe_type", "type", text="Keyframe Type") + layout.operator_menu_enum("action.handle_type", "type", text="Handle Type") + layout.operator_menu_enum("action.interpolation_type", "type", text="Interpolation Mode") + layout.operator_menu_enum("action.extrapolation_type", "type", text="Extrapolation Mode") layout.separator() layout.operator("action.clean") @@ -186,9 +190,10 @@ class DOPESHEET_MT_key_transform(bpy.types.Menu): layout = self.layout layout.column() - layout.operator("transform.translate", text="Grab/Move") + layout.operator("transform.transform", text="Grab/Move").mode = 'TIME_TRANSLATE' layout.operator("transform.transform", text="Extend").mode = 'TIME_EXTEND' - layout.operator("transform.resize", text="Scale") + layout.operator("transform.transform", text="Slide").mode = 'TIME_SLIDE' + layout.operator("transform.transform", text="Scale").mode = 'TIME_SCALE' classes = [ diff --git a/release/scripts/ui/space_graph.py b/release/scripts/ui/space_graph.py index 64f6337177e..1533b891202 100644 --- a/release/scripts/ui/space_graph.py +++ b/release/scripts/ui/space_graph.py @@ -114,6 +114,7 @@ class GRAPH_MT_select(bpy.types.Menu): layout.separator() layout.operator("graph.select_border") layout.operator("graph.select_border", text="Border Axis Range").axis_range = True + layout.operator("graph.select_border", text="Border (Include Handles)").include_handles = True layout.separator() layout.operator("graph.select_column", text="Columns on Selected Keys").mode = 'KEYS' @@ -126,6 +127,9 @@ class GRAPH_MT_select(bpy.types.Menu): layout.operator("graph.select_more") layout.operator("graph.select_less") + layout.separator() + layout.operator("graph.select_linked") + class GRAPH_MT_channel(bpy.types.Menu): bl_label = "Channel" @@ -142,6 +146,7 @@ class GRAPH_MT_channel(bpy.types.Menu): layout.separator() layout.operator("anim.channels_editable_toggle") + layout.operator("anim.channels_visibility_set") layout.separator() layout.operator("anim.channels_expand") @@ -160,8 +165,8 @@ class GRAPH_MT_key(bpy.types.Menu): layout.column() layout.menu("GRAPH_MT_key_transform", text="Transform") - layout.operator_menu_enum("graph.snap", property="type", text="Snap") - layout.operator_menu_enum("graph.mirror", property="type", text="Mirror") + layout.operator_menu_enum("graph.snap", "type", text="Snap") + layout.operator_menu_enum("graph.mirror", "type", text="Mirror") layout.separator() layout.operator("graph.keyframe_insert") @@ -172,9 +177,9 @@ class GRAPH_MT_key(bpy.types.Menu): layout.operator("graph.delete") layout.separator() - layout.operator_menu_enum("graph.handle_type", property="type", text="Handle Type") - layout.operator_menu_enum("graph.interpolation_type", property="type", text="Interpolation Mode") - layout.operator_menu_enum("graph.extrapolation_type", property="type", text="Extrapolation Mode") + layout.operator_menu_enum("graph.handle_type", "type", text="Handle Type") + layout.operator_menu_enum("graph.interpolation_type", "type", text="Interpolation Mode") + layout.operator_menu_enum("graph.extrapolation_type", "type", text="Extrapolation Mode") layout.separator() layout.operator("graph.clean") diff --git a/release/scripts/ui/space_image.py b/release/scripts/ui/space_image.py index 3fd7025a4f7..e9c963db7b3 100644 --- a/release/scripts/ui/space_image.py +++ b/release/scripts/ui/space_image.py @@ -19,7 +19,7 @@ # <pep8 compliant> import bpy -narrowui = 180 +narrowui = bpy.context.user_preferences.view.properties_width_check class IMAGE_MT_view(bpy.types.Menu): @@ -29,7 +29,7 @@ class IMAGE_MT_view(bpy.types.Menu): layout = self.layout sima = context.space_data - # uv = sima.uv_editor + uv = sima.uv_editor toolsettings = context.tool_settings show_uvedit = sima.show_uvedit @@ -42,6 +42,7 @@ class IMAGE_MT_view(bpy.types.Menu): layout.prop(sima, "update_automatically") if show_uvedit: layout.prop(toolsettings, "uv_local_view") # Numpad / + layout.prop(uv, "draw_other_objects") layout.separator() @@ -111,10 +112,13 @@ class IMAGE_MT_image(bpy.types.Menu): layout.operator("image.save") layout.operator("image.save_as") + layout.operator("image.save_as", text="Save a Copy").copy = True if ima.source == 'SEQUENCE': layout.operator("image.save_sequence") + layout.operator("image.external_edit", "Edit Externally") + if not show_render: layout.separator() @@ -141,8 +145,8 @@ class IMAGE_MT_uvs_showhide(bpy.types.Menu): layout = self.layout layout.operator("uv.reveal") - layout.operator("uv.hide") - layout.operator("uv.hide").unselected = True + layout.operator("uv.hide", text="Hide Selected") + layout.operator("uv.hide", text="Hide Unselected").unselected = True class IMAGE_MT_uvs_transform(bpy.types.Menu): @@ -250,7 +254,7 @@ class IMAGE_HT_header(bpy.types.Header): iuser = sima.image_user toolsettings = context.tool_settings - # show_render = sima.show_render + show_render = sima.show_render # show_paint = sima.show_paint show_uvedit = sima.show_uvedit @@ -274,6 +278,8 @@ class IMAGE_HT_header(bpy.types.Header): sub.menu("IMAGE_MT_uvs") layout.template_ID(sima, "image", new="image.new") + if not show_render: + layout.prop(sima, "image_pin", text="") # uv editing if show_uvedit: @@ -340,7 +346,7 @@ class IMAGE_PT_image_properties(bpy.types.Panel): # ima = sima.image iuser = sima.image_user - layout.template_image(sima, "image", iuser, compact=True) + layout.template_image(sima, "image", iuser) class IMAGE_PT_game_properties(bpy.types.Panel): @@ -402,7 +408,81 @@ class IMAGE_PT_view_histogram(bpy.types.Panel): sima = context.space_data - layout.template_histogram(sima, "histogram") + layout.template_histogram(sima.scopes, "histogram") + layout.prop(sima.scopes.histogram, "mode", icon_only=True) + + +class IMAGE_PT_view_waveform(bpy.types.Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'PREVIEW' + bl_label = "Waveform" + + def poll(self, context): + sima = context.space_data + return (sima and sima.image) + + def draw(self, context): + layout = self.layout + + sima = context.space_data + layout.template_waveform(sima, "scopes") + sub = layout.row().split(percentage=0.75) + sub.prop(sima.scopes, "waveform_alpha") + sub.prop(sima.scopes, "waveform_mode", text="", icon_only=True) + + +class IMAGE_PT_view_vectorscope(bpy.types.Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'PREVIEW' + bl_label = "Vectorscope" + + def poll(self, context): + sima = context.space_data + return (sima and sima.image) + + def draw(self, context): + layout = self.layout + + sima = context.space_data + layout.template_vectorscope(sima, "scopes") + layout.prop(sima.scopes, "vectorscope_alpha") + + +class IMAGE_PT_sample_line(bpy.types.Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'PREVIEW' + bl_label = "Sample Line" + + def poll(self, context): + sima = context.space_data + return (sima and sima.image) + + def draw(self, context): + layout = self.layout + layout.operator("image.sample_line") + sima = context.space_data + layout.template_histogram(sima, "sample_histogram") + layout.prop(sima.sample_histogram, "mode") + + +class IMAGE_PT_scope_sample(bpy.types.Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'PREVIEW' + bl_label = "Scope Samples" + + def poll(self, context): + sima = context.space_data + return sima + + def draw(self, context): + layout = self.layout + sima = context.space_data + split = layout.split() + row = split.row() + row.prop(sima.scopes, "use_full_resolution") + row = split.row() + row.active = not sima.scopes.use_full_resolution + row.prop(sima.scopes, "accuracy") class IMAGE_PT_view_properties(bpy.types.Panel): @@ -443,6 +523,9 @@ class IMAGE_PT_view_properties(bpy.types.Panel): if show_uvedit: col = layout.column() + col.prop(uvedit, "cursor_location") + + col = layout.column() col.label(text="UVs:") row = col.row() if wide_ui: @@ -570,7 +653,7 @@ class IMAGE_PT_paint_curve(bpy.types.Panel): brush = toolsettings.brush layout.template_curve_mapping(brush, "curve") - layout.operator_menu_enum("brush.curve_preset", property="shape") + layout.operator_menu_enum("brush.curve_preset", "shape") classes = [ @@ -590,7 +673,11 @@ classes = [ IMAGE_PT_paint_curve, IMAGE_PT_game_properties, IMAGE_PT_view_properties, - IMAGE_PT_view_histogram] + IMAGE_PT_view_histogram, + IMAGE_PT_view_waveform, + IMAGE_PT_view_vectorscope, + IMAGE_PT_sample_line, + IMAGE_PT_scope_sample] def register(): diff --git a/release/scripts/ui/space_info.py b/release/scripts/ui/space_info.py index e1bc8f2b226..ca8371b85fe 100644 --- a/release/scripts/ui/space_info.py +++ b/release/scripts/ui/space_info.py @@ -25,7 +25,7 @@ class INFO_HT_header(bpy.types.Header): def draw(self, context): layout = self.layout - + wm = context.manager if wm and len(wm.operators): last_op = wm.operators[-1] @@ -64,11 +64,10 @@ class INFO_HT_header(bpy.types.Header): layout.separator() layout.template_running_jobs() - - if last_op and last_op.has_reports: - layout.template_reports_banner(last_op) - else: - layout.label(text=scene.statistics()) + + layout.template_reports_banner() + + layout.label(text=scene.statistics()) # XXX: this should be right-aligned to the RHS of the region layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER', text="") @@ -85,7 +84,7 @@ class INFO_MT_file(bpy.types.Menu): layout.operator_context = 'INVOKE_AREA' layout.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER') layout.menu("INFO_MT_file_open_recent") - layout.operator("wm.recover_last_session") + layout.operator("wm.recover_last_session", icon='RECOVER_LAST') layout.operator("wm.recover_auto_save", text="Recover Auto Save...") layout.separator() @@ -106,7 +105,9 @@ class INFO_MT_file(bpy.types.Menu): layout.operator_context = 'INVOKE_AREA' layout.operator("wm.link_append", text="Link") - layout.operator("wm.link_append", text="Append").link = False + props = layout.operator("wm.link_append", text="Append") + props.link = False + props.instance_groups = False layout.separator() @@ -123,27 +124,6 @@ class INFO_MT_file(bpy.types.Menu): layout.operator("wm.exit_blender", text="Quit", icon='QUIT') -class INFO_MT_file_open_recent(bpy.types.Menu): - bl_idname = "INFO_MT_file_open_recent" - bl_label = "Open Recent..." - - def draw(self, context): - import os - layout = self.layout - layout.operator_context = 'EXEC_AREA' - - path = os.path.join(bpy.app.home, ".Blog") - - if os.path.isfile(path): - file = open(path, "rU") - for line in file: - line = line.rstrip() - layout.operator("wm.open_mainfile", text=line, icon='FILE_BLEND').path = line - file.close() - else: - layout.label(text='No recent files') - - class INFO_MT_file_import(bpy.types.Menu): bl_idname = "INFO_MT_file_import" bl_label = "Import" @@ -198,6 +178,35 @@ class INFO_MT_mesh_add(bpy.types.Menu): layout.operator("mesh.primitive_monkey_add", icon='MESH_MONKEY', text="Monkey") +class INFO_MT_curve_add(bpy.types.Menu): + bl_idname = "INFO_MT_curve_add" + bl_label = "Curve" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("curve.primitive_bezier_curve_add", icon='CURVE_BEZCURVE', text="Bezier") + layout.operator("curve.primitive_bezier_circle_add", icon='CURVE_BEZCIRCLE', text="Circle") + layout.operator("curve.primitive_nurbs_curve_add", icon='CURVE_NCURVE', text="Nurbs Curve") + layout.operator("curve.primitive_nurbs_circle_add", icon='CURVE_NCIRCLE', text="Nurbs Circle") + layout.operator("curve.primitive_nurbs_path_add", icon='CURVE_PATH', text="Path") + + +class INFO_MT_surface_add(bpy.types.Menu): + bl_idname = "INFO_MT_surface_add" + bl_label = "Surface" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("surface.primitive_nurbs_surface_curve_add", icon='SURFACE_NCURVE', text="NURBS Curve") + layout.operator("surface.primitive_nurbs_surface_circle_add", icon='SURFACE_NCIRCLE', text="NURBS Circle") + layout.operator("surface.primitive_nurbs_surface_surface_add", icon='SURFACE_NSURFACE', text="NURBS Surface") + layout.operator("surface.primitive_nurbs_surface_tube_add", icon='SURFACE_NTUBE', text="NURBS Tube") + layout.operator("surface.primitive_nurbs_surface_sphere_add", icon='SURFACE_NSPHERE', text="NURBS Sphere") + layout.operator("surface.primitive_nurbs_surface_donut_add", icon='SURFACE_NDONUT', text="NURBS Torus") + + class INFO_MT_armature_add(bpy.types.Menu): bl_idname = "INFO_MT_armature_add" bl_label = "Armature" @@ -219,9 +228,11 @@ class INFO_MT_add(bpy.types.Menu): #layout.operator_menu_enum("object.mesh_add", "type", text="Mesh", icon='OUTLINER_OB_MESH') layout.menu("INFO_MT_mesh_add", icon='OUTLINER_OB_MESH') - layout.operator_menu_enum("object.curve_add", "type", text="Curve", icon='OUTLINER_OB_CURVE') - layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE') - layout.operator_menu_enum("object.metaball_add", "type", 'META', text="Metaball", icon='OUTLINER_OB_META') + #layout.operator_menu_enum("object.curve_add", "type", text="Curve", icon='OUTLINER_OB_CURVE') + layout.menu("INFO_MT_curve_add", icon='OUTLINER_OB_CURVE') + #layout.operator_menu_enum("object.surface_add", "type", text="Surface", icon='OUTLINER_OB_SURFACE') + layout.menu("INFO_MT_surface_add", icon='OUTLINER_OB_SURFACE') + layout.operator_menu_enum("object.metaball_add", "type", text="Metaball", icon='OUTLINER_OB_META') layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT') layout.separator() @@ -233,17 +244,17 @@ class INFO_MT_add(bpy.types.Menu): layout.operator("object.camera_add", text="Camera", icon='OUTLINER_OB_CAMERA') layout.operator_context = 'EXEC_SCREEN' - layout.operator_menu_enum("object.lamp_add", "type", 'LAMP', text="Lamp", icon='OUTLINER_OB_LAMP') + layout.operator_menu_enum("object.lamp_add", "type", text="Lamp", icon='OUTLINER_OB_LAMP') layout.separator() - layout.operator_menu_enum("object.effector_add", "type", 'EMPTY', text="Force Field", icon='OUTLINER_OB_EMPTY') + layout.operator_menu_enum("object.effector_add", "type", text="Force Field", icon='OUTLINER_OB_EMPTY') layout.separator() if(len(bpy.data.groups) > 10): layout.operator_context = 'INVOKE_DEFAULT' - layout.operator("object.group_instance_add", "type", text="Group Instance...", icon='OUTLINER_OB_EMPTY') + layout.operator("object.group_instance_add", text="Group Instance...", icon='OUTLINER_OB_EMPTY') else: - layout.operator_menu_enum("object.group_instance_add", "type", text="Group Instance", icon='OUTLINER_OB_EMPTY') + layout.operator_menu_enum("object.group_instance_add", "group", text="Group Instance", icon='OUTLINER_OB_EMPTY') class INFO_MT_game(bpy.types.Menu): @@ -263,6 +274,8 @@ class INFO_MT_game(bpy.types.Menu): layout.prop(gs, "show_physics_visualization") layout.prop(gs, "use_deprecation_warnings") layout.prop(gs, "use_animation_record") + layout.separator() + layout.prop(gs, "auto_start") class INFO_MT_render(bpy.types.Menu): @@ -273,18 +286,18 @@ class INFO_MT_render(bpy.types.Menu): # rd = context.scene.render - layout.operator("screen.render", text="Render Image", icon='RENDER_STILL') - layout.operator("screen.render", text="Render Animation", icon='RENDER_ANIMATION').animation = True + layout.operator("render.render", text="Render Image", icon='RENDER_STILL') + layout.operator("render.render", text="Render Animation", icon='RENDER_ANIMATION').animation = True layout.separator() - layout.operator("screen.opengl_render", text="OpenGL Render Image") - layout.operator("screen.opengl_render", text="OpenGL Render Animation").animation = True + layout.operator("render.opengl", text="OpenGL Render Image") + layout.operator("render.opengl", text="OpenGL Render Animation").animation = True layout.separator() - layout.operator("screen.render_view_show") - layout.operator("screen.play_rendered_anim") + layout.operator("render.view_show") + layout.operator("render.play_rendered_anim") class INFO_MT_help(bpy.types.Menu): @@ -293,92 +306,30 @@ class INFO_MT_help(bpy.types.Menu): def draw(self, context): layout = self.layout - layout.operator("help.manual", icon='HELP') - layout.operator("help.release_logs", icon='URL') + 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-250/' layout.separator() - layout.operator("help.blender_website", icon='URL') - layout.operator("help.blender_eshop", icon='URL') - layout.operator("help.developer_community", icon='URL') - layout.operator("help.user_community", icon='URL') + layout.operator("wm.url_open", text="Blender Website", icon='URL').url = 'http://www.blender.org/' + layout.operator("wm.url_open", text="Blender e-Shop", icon='URL').url = 'http://www.blender.org/e-shop' + layout.operator("wm.url_open", text="Developer Community", icon='URL').url = 'http://www.blender.org/community/get-involved/' + layout.operator("wm.url_open", text="User Community", icon='URL').url = 'http://www.blender.org/community/user-community/' layout.separator() - layout.operator("help.report_bug", icon='URL') + 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("help.python_api", icon='URL') + layout.operator("wm.url_open", text="Python API Reference", icon='URL').url = 'http://www.blender.org/documentation/250PythonDoc/contents.html' layout.operator("help.operator_cheat_sheet") + layout.separator() + layout.operator("wm.splash") # Help operators -class HelpOperator(bpy.types.Operator): - - def execute(self, context): - import webbrowser - webbrowser.open(self._url) - return {'FINISHED'} - - -class HELP_OT_manual(HelpOperator): - '''The Blender Wiki manual''' - bl_idname = "help.manual" - bl_label = "Manual" - _url = 'http://wiki.blender.org/index.php/Doc:Manual' - - -class HELP_OT_release_logs(HelpOperator): - '''Information about the changes in this version of Blender''' - bl_idname = "help.release_logs" - bl_label = "Release Log" - _url = 'http://www.blender.org/development/release-logs/blender-250/' - - -class HELP_OT_blender_website(HelpOperator): - '''The official Blender website''' - bl_idname = "help.blender_website" - bl_label = "Blender Website" - _url = 'http://www.blender.org/' - - -class HELP_OT_blender_eshop(HelpOperator): - '''Buy official Blender resources and merchandise online''' - bl_idname = "help.blender_eshop" - bl_label = "Blender e-Shop" - _url = 'http://www.blender3d.org/e-shop' - - -class HELP_OT_developer_community(HelpOperator): - '''Get involved with Blender development''' - bl_idname = "help.developer_community" - bl_label = "Developer Community" - _url = 'http://www.blender.org/community/get-involved/' - - -class HELP_OT_user_community(HelpOperator): - '''Get involved with other Blender users''' - bl_idname = "help.user_community" - bl_label = "User Community" - _url = 'http://www.blender.org/community/user-community/' - - -class HELP_OT_report_bug(HelpOperator): - '''Report a bug in the Blender bug tracker''' - bl_idname = "help.report_bug" - bl_label = "Report a Bug" - _url = 'http://projects.blender.org/tracker/?atid=498&group_id=9&func=browse' - - -class HELP_OT_python_api(HelpOperator): - '''Reference for operator and data Python API''' - bl_idname = "help.python_api" - bl_label = "Python API Reference" - _url = 'http://www.blender.org/documentation/250PythonDoc/contents.html' - - class HELP_OT_operator_cheat_sheet(bpy.types.Operator): bl_idname = "help.operator_cheat_sheet" - bl_label = "Operator Cheat Sheet (new textblock)" + bl_label = "Operator Cheat Sheet" def execute(self, context): op_strings = [] @@ -404,25 +355,18 @@ class HELP_OT_operator_cheat_sheet(bpy.types.Operator): classes = [ INFO_HT_header, INFO_MT_file, - INFO_MT_file_open_recent, INFO_MT_file_import, INFO_MT_file_export, INFO_MT_file_external_data, INFO_MT_add, INFO_MT_mesh_add, + INFO_MT_curve_add, + INFO_MT_surface_add, INFO_MT_armature_add, INFO_MT_game, INFO_MT_render, INFO_MT_help, - HELP_OT_manual, - HELP_OT_release_logs, - HELP_OT_blender_website, - HELP_OT_blender_eshop, - HELP_OT_developer_community, - HELP_OT_user_community, - HELP_OT_report_bug, - HELP_OT_python_api, HELP_OT_operator_cheat_sheet] diff --git a/release/scripts/ui/space_logic.py b/release/scripts/ui/space_logic.py index 07b26fdc862..a01a5ce6305 100644 --- a/release/scripts/ui/space_logic.py +++ b/release/scripts/ui/space_logic.py @@ -35,20 +35,31 @@ class LOGIC_PT_properties(bpy.types.Panel): ob = context.active_object game = ob.game - layout.operator("object.game_property_new", text="Add Game Property") + layout.operator("object.game_property_new", text="Add Game Property", icon='ZOOMIN') for i, prop in enumerate(game.properties): - row = layout.row(align=True) + box = layout.box() + row = box.row() row.prop(prop, "name", text="") row.prop(prop, "type", text="") row.prop(prop, "value", text="", toggle=True) # we dont care about the type. rna will display correctly row.prop(prop, "debug", text="", toggle=True, icon='INFO') - row.operator("object.game_property_remove", text="", icon='X').index = i + row.operator("object.game_property_remove", text="", icon='X', emboss=False).index = i +class LOGIC_MT_logicbricks_add(bpy.types.Menu): + bl_label = "Add" + + def draw(self, context): + layout = self.layout + + layout.operator_menu_enum("logic.sensor_add", "type", text="Sensor") + layout.operator_menu_enum("logic.controller_add", "type", text="Controller") + layout.operator_menu_enum("logic.actuator_add", "type", text="Actuator") + classes = [ - LOGIC_PT_properties] + LOGIC_PT_properties, LOGIC_MT_logicbricks_add] def register(): diff --git a/release/scripts/ui/space_nla.py b/release/scripts/ui/space_nla.py index 12ed8aa7740..0eb342d4b15 100644 --- a/release/scripts/ui/space_nla.py +++ b/release/scripts/ui/space_nla.py @@ -102,7 +102,7 @@ class NLA_MT_edit(bpy.types.Menu): layout.column() layout.menu("NLA_MT_edit_transform", text="Transform") - layout.operator_menu_enum("nla.snap", property="type", text="Snap") + layout.operator_menu_enum("nla.snap", "type", text="Snap") layout.separator() layout.operator("nla.duplicate") diff --git a/release/scripts/ui/space_node.py b/release/scripts/ui/space_node.py index f510235a39d..365835009ba 100644 --- a/release/scripts/ui/space_node.py +++ b/release/scripts/ui/space_node.py @@ -55,7 +55,10 @@ class NODE_HT_header(bpy.types.Header): snode_id = snode.id id_from = snode.id_from if id_from: - layout.template_ID(id_from, "active_texture", new="texture.new") + if snode.texture_type == 'BRUSH': + layout.template_ID(id_from, "texture", new="texture.new") + else: + layout.template_ID(id_from, "active_texture", new="texture.new") if snode_id: layout.prop(snode_id, "use_nodes") @@ -66,6 +69,10 @@ class NODE_HT_header(bpy.types.Header): layout.prop(scene.render, "free_unused_nodes", text="Free Unused") layout.prop(snode, "backdrop") + layout.separator() + + layout.template_running_jobs() + class NODE_MT_view(bpy.types.Menu): bl_label = "View" @@ -101,6 +108,9 @@ class NODE_MT_select(bpy.types.Menu): layout.operator("node.select_all") layout.operator("node.select_linked_from") layout.operator("node.select_linked_to") + layout.operator("node.select_same_type") + layout.operator("node.select_same_type_next") + layout.operator("node.select_same_type_prev") class NODE_MT_node(bpy.types.Menu): @@ -129,8 +139,10 @@ class NODE_MT_node(bpy.types.Menu): layout.separator() - layout.operator("node.hide") - layout.operator("node.mute") + layout.operator("node.hide_toggle") + layout.operator("node.mute_toggle") + layout.operator("node.preview_toggle") + layout.operator("node.hide_socket_toggle") # XXX # layout.operator("node.rename") diff --git a/release/scripts/ui/space_outliner.py b/release/scripts/ui/space_outliner.py index d5bb35baa6c..ec3c430feaa 100644 --- a/release/scripts/ui/space_outliner.py +++ b/release/scripts/ui/space_outliner.py @@ -36,11 +36,14 @@ class OUTLINER_HT_header(bpy.types.Header): if context.area.show_menus: sub = row.row(align=True) sub.menu("OUTLINER_MT_view") + sub.menu("OUTLINER_MT_search") if space.display_mode == 'DATABLOCKS': sub.menu("OUTLINER_MT_edit_datablocks") layout.prop(space, "display_mode", text="") + layout.prop(space, "display_filter", icon='VIEWZOOM', text="") + layout.separator() if space.display_mode == 'DATABLOCKS': @@ -83,6 +86,20 @@ class OUTLINER_MT_view(bpy.types.Menu): layout.operator("screen.screen_full_area") +class OUTLINER_MT_search(bpy.types.Menu): + bl_label = "Search" + + def draw(self, context): + layout = self.layout + + space = context.space_data + + col = layout.column() + + col.prop(space, "match_case_sensitive") + col.prop(space, "match_complete") + + class OUTLINER_MT_edit_datablocks(bpy.types.Menu): bl_label = "Edit" @@ -103,6 +120,7 @@ class OUTLINER_MT_edit_datablocks(bpy.types.Menu): classes = [ OUTLINER_HT_header, OUTLINER_MT_view, + OUTLINER_MT_search, OUTLINER_MT_edit_datablocks] diff --git a/release/scripts/ui/space_sequencer.py b/release/scripts/ui/space_sequencer.py index 8930a04e11c..97d84b77e18 100644 --- a/release/scripts/ui/space_sequencer.py +++ b/release/scripts/ui/space_sequencer.py @@ -50,10 +50,10 @@ class SEQUENCER_HT_header(bpy.types.Header): sub.menu("SEQUENCER_MT_add") sub.menu("SEQUENCER_MT_strip") - layout.prop(st, "view_type", text="") + layout.prop(st, "view_type", expand=True, text="") if (st.view_type == 'PREVIEW') or (st.view_type == 'SEQUENCER_PREVIEW'): - layout.prop(st, "display_mode", text="") + layout.prop(st, "display_mode", expand=True, text="") if (st.view_type == 'SEQUENCER'): row = layout.row(align=True) @@ -69,6 +69,14 @@ class SEQUENCER_HT_header(bpy.types.Header): else: layout.prop(st, "display_channel", text="Channel") + ed = context.scene.sequence_editor + if ed: + row = layout.row(align=True) + row.prop(ed, "show_overlay", text="", icon='GHOST_ENABLED') + if ed.show_overlay: + row.prop(ed, "overlay_frame", text="") + row.prop(ed, "overlay_lock", text="", icon='LOCKED') + class SEQUENCER_MT_view_toggle(bpy.types.Menu): bl_label = "View Type" @@ -126,6 +134,10 @@ class SEQUENCER_MT_view(bpy.types.Menu): layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.operator("sequencer.view_all_preview", text='Fit preview in window') layout.operator_context = 'INVOKE_DEFAULT' + + # # XXX, invokes in the header view + # layout.operator("sequencer.view_ghost_border", text='Overlay Border') + layout.operator("sequencer.view_selected") layout.prop(st, "draw_frames") @@ -136,6 +148,8 @@ class SEQUENCER_MT_view(bpy.types.Menu): layout.prop(st, "separate_color_preview") layout.separator() + layout.prop(st, "use_marker_sync") + layout.separator() layout.operator("screen.area_dupli") layout.operator("screen.screen_full_area") @@ -185,7 +199,7 @@ class SEQUENCER_MT_add(bpy.types.Menu): layout.operator_context = 'INVOKE_REGION_WIN' layout.column() - layout.operator("sequencer.scene_strip_add", text="Scene") + layout.operator_menu_enum("sequencer.scene_strip_add", "scene", text="Scene...") layout.operator("sequencer.movie_strip_add", text="Movie") layout.operator("sequencer.image_strip_add", text="Image") layout.operator("sequencer.sound_strip_add", text="Sound") @@ -215,6 +229,7 @@ class SEQUENCER_MT_add_effect(bpy.types.Menu): layout.operator("sequencer.effect_strip_add", text="Transform").type = 'TRANSFORM' layout.operator("sequencer.effect_strip_add", text="Color").type = 'COLOR' layout.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED' + layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM' class SEQUENCER_MT_strip(bpy.types.Menu): @@ -234,6 +249,7 @@ class SEQUENCER_MT_strip(bpy.types.Menu): layout.operator("sequencer.cut", text="Cut (hard) at frame").type = 'HARD' layout.operator("sequencer.cut", text="Cut (soft) at frame").type = 'SOFT' layout.operator("sequencer.images_separate") + layout.operator("sequencer.deinterlace_selected_movies") layout.separator() layout.operator("sequencer.duplicate") @@ -244,20 +260,23 @@ class SEQUENCER_MT_strip(bpy.types.Menu): if strip: stype = strip.type - if stype == 'EFFECT': - layout.separator() - layout.operator("sequencer.effect_change") - layout.operator("sequencer.effect_reassign_inputs") + # XXX note strip.type is never equal to 'EFFECT', look at seq_type_items within rna_sequencer.c + if stype == 'EFFECT': + pass + # layout.separator() + # layout.operator("sequencer.effect_change") + # layout.operator("sequencer.effect_reassign_inputs") elif stype == 'IMAGE': layout.separator() # layout.operator("sequencer.image_change") layout.operator("sequencer.rendersize") elif stype == 'SCENE': - layout.separator() - layout.operator("sequencer.scene_change", text="Change Scene") + pass + # layout.separator() + # layout.operator("sequencer.scene_change", text="Change Scene") elif stype == 'MOVIE': layout.separator() - layout.operator("sequencer.movie_change") + # layout.operator("sequencer.movie_change") layout.operator("sequencer.rendersize") layout.separator() @@ -272,6 +291,7 @@ class SEQUENCER_MT_strip(bpy.types.Menu): layout.separator() layout.operator("sequencer.reload") + layout.operator("sequencer.reassign_inputs") layout.separator() layout.operator("sequencer.lock") layout.operator("sequencer.unlock") @@ -284,6 +304,10 @@ class SEQUENCER_MT_strip(bpy.types.Menu): layout.operator_menu_enum("sequencer.swap", "side") + layout.separator() + + layout.operator("sequencer.swap_data") + class SequencerButtonsPanel(bpy.types.Panel): bl_space_type = 'SEQUENCE_EDITOR' @@ -312,7 +336,9 @@ class SEQUENCER_PT_edit(SequencerButtonsPanel): def draw(self, context): layout = self.layout + scene = context.scene render = context.scene.render + frame_current = scene.frame_current strip = act_strip(context) split = layout.split(percentage=0.3) @@ -327,38 +353,54 @@ class SEQUENCER_PT_edit(SequencerButtonsPanel): split.label(text="Blend:") split.prop(strip, "blend_mode", text="") - row = layout.row() - if strip.mute == True: - row.prop(strip, "mute", toggle=True, icon='RESTRICT_VIEW_ON', text="") - elif strip.mute is False: - row.prop(strip, "mute", toggle=True, icon='RESTRICT_VIEW_OFF', text="") - + row = layout.row(align=True) sub = row.row() sub.active = (not strip.mute) - sub.prop(strip, "blend_opacity", text="Opacity", slider=True) - - row = layout.row() - row.prop(strip, "lock") - row.prop(strip, "frame_locked", text="Frame Lock") + sub = row.row() + row.prop(strip, "mute", toggle=True, icon='RESTRICT_VIEW_ON' if strip.mute else 'RESTRICT_VIEW_OFF', text="") + row.prop(strip, "lock", toggle=True, icon='LOCKED' if strip.lock else 'UNLOCKED', text="") col = layout.column() - col.enabled = not strip.lock - col.prop(strip, "channel") - col.prop(strip, "start_frame") - subrow = col.split(percentage=0.66) - subrow.prop(strip, "length") - subrow.label(text="%.2f sec" % (strip.length / (render.fps / render.fps_base))) + sub = col.column() + sub.enabled = not strip.lock + sub.prop(strip, "channel") + sub.prop(strip, "frame_start") + sub.prop(strip, "frame_final_length") col = layout.column(align=True) - col.label(text="Offset:") - col.prop(strip, "start_offset", text="Start") - col.prop(strip, "end_offset", text="End") + row = col.row() + row.label(text="Final Length: %s" % bpy.utils.smpte_from_frame(strip.frame_final_length)) + row = col.row() + row.active = (frame_current >= strip.frame_start and frame_current <= strip.frame_start + strip.frame_length) + row.label(text="Playhead: %d" % (frame_current - strip.frame_start)) + + col.label(text="Frame Offset %d:%d" % (strip.frame_offset_start, strip.frame_offset_end)) + col.label(text="Frame Still %d:%d" % (strip.frame_still_start, strip.frame_still_end)) - col = layout.column(align=True) - col.label(text="Still:") - col.prop(strip, "start_still", text="Start") - col.prop(strip, "end_still", text="End") + +class SEQUENCER_PT_preview(bpy.types.Panel): + bl_label = "Scene Preview/Render" + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'UI' + + def draw(self, context): + layout = self.layout + render = context.scene.render + + col = layout.column() + col.prop(render, "use_sequencer_gl_preview", text="Open GL Preview") + col = layout.column() + col.active = render.use_sequencer_gl_preview + col.prop(render, "sequencer_gl_preview", text="") + + ''' + col = layout.column() + col.prop(render, "use_sequencer_gl_render", text="Open GL Render") + col = layout.column() + col.active = render.use_sequencer_gl_render + col.prop(render, "sequencer_gl_render", text="") + ''' class SEQUENCER_PT_effect(SequencerButtonsPanel): @@ -375,7 +417,8 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel): return strip.type in ('ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', - 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED') + 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED', + 'MULTICAM') def draw(self, context): layout = self.layout @@ -419,6 +462,22 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel): elif strip.type == 'TRANSFORM': self.draw_panel_transform(strip) + elif strip.type == "MULTICAM": + layout.prop(strip, "multicam_source") + + row = layout.row(align=True) + sub = row.row() + sub.scale_x = 2.0 + + if not context.screen.animation_playing: + sub.operator("screen.animation_play", text="", icon='PLAY') + else: + sub.operator("screen.animation_play", text="", icon='PAUSE') + + row.label("Cut To") + for i in range(1, strip.channel): + row.operator("sequencer.cut_multicam", text=str(i)).camera = i + col = layout.column(align=True) if strip.type == 'SPEED': @@ -470,11 +529,16 @@ class SEQUENCER_PT_input(SequencerButtonsPanel): if not strip: return False - return strip.type in ('MOVIE', 'IMAGE') - + 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') + def draw_filename(self, context): pass - + def draw(self, context): layout = self.layout @@ -483,16 +547,14 @@ class SEQUENCER_PT_input(SequencerButtonsPanel): self.draw_filename(context) layout.prop(strip, "use_translation", text="Image Offset:") - if strip.transform: + if strip.use_translation: col = layout.column(align=True) - col.active = strip.use_translation col.prop(strip.transform, "offset_x", text="X") col.prop(strip.transform, "offset_y", text="Y") layout.prop(strip, "use_crop", text="Image Crop:") - if strip.crop: + if strip.use_crop: col = layout.column(align=True) - col.active = strip.use_crop col.prop(strip.crop, "top") col.prop(strip.crop, "left") col.prop(strip.crop, "bottom") @@ -502,7 +564,8 @@ class SEQUENCER_PT_input(SequencerButtonsPanel): col.label(text="Trim Duration:") col.prop(strip, "animation_start_offset", text="Start") col.prop(strip, "animation_end_offset", text="End") - + + class SEQUENCER_PT_input_movie(SEQUENCER_PT_input): bl_label = "Strip Input" @@ -527,6 +590,7 @@ class SEQUENCER_PT_input_movie(SEQUENCER_PT_input): col = split.column() col.prop(strip, "filepath", text="") + class SEQUENCER_PT_input_image(SEQUENCER_PT_input): bl_label = "Strip Input" @@ -553,7 +617,7 @@ class SEQUENCER_PT_input_image(SEQUENCER_PT_input): # Current element for the filename - elem = strip.getStripElem(context.scene.current_frame) + elem = strip.getStripElem(context.scene.frame_current) if elem: split = layout.split(percentage=0.2) col = split.column() @@ -562,6 +626,23 @@ class SEQUENCER_PT_input_image(SEQUENCER_PT_input): col.prop(elem, "filename", text="") # strip.elements[0] could be a fallback +class SEQUENCER_PT_input_secondary(SEQUENCER_PT_input): + bl_label = "Strip Input" + + def poll(self, context): + if not self.has_sequencer(context): + return False + + strip = act_strip(context) + if not strip: + return False + + return strip.type in ('SCENE', 'META') + + def draw_filename(self, context): + pass + + class SEQUENCER_PT_sound(SequencerButtonsPanel): bl_label = "Sound" @@ -583,7 +664,7 @@ class SEQUENCER_PT_sound(SequencerButtonsPanel): layout.template_ID(strip, "sound", open="sound.open") layout.separator() - layout.prop(strip.sound, "filepath", text="") + layout.prop(strip, "filepath", text="") row = layout.row() if strip.sound.packed_file: @@ -594,6 +675,12 @@ class SEQUENCER_PT_sound(SequencerButtonsPanel): row.prop(strip.sound, "caching") layout.prop(strip, "volume") + layout.prop(strip, "attenuation") + + col = layout.column(align=True) + col.label(text="Trim Duration:") + col.prop(strip, "animation_start_offset", text="Start") + col.prop(strip, "animation_end_offset", text="End") class SEQUENCER_PT_scene(SequencerButtonsPanel): @@ -616,6 +703,9 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel): layout.template_ID(strip, "scene") + layout.label(text="Camera Override") + layout.template_ID(strip, "scene_camera") + class SEQUENCER_PT_filter(SequencerButtonsPanel): bl_label = "Filter" @@ -628,7 +718,12 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel): 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') def draw(self, context): layout = self.layout @@ -638,34 +733,37 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel): col = layout.column() col.label(text="Video:") col.prop(strip, "strobe") + + row = layout.row() + row.label(text="Flip:") + row.prop(strip, "flip_x", text="X") + row.prop(strip, "flip_y", text="Y") + + col = layout.column() + col.prop(strip, "reverse_frames", text="Backwards") col.prop(strip, "de_interlace") col = layout.column() col.label(text="Colors:") + col.prop(strip, "color_saturation", text="Saturation") col.prop(strip, "multiply_colors", text="Multiply") col.prop(strip, "premultiply") col.prop(strip, "convert_float") - col = layout.column() - col.label(text="Flip:") - col.prop(strip, "flip_x", text="X") - col.prop(strip, "flip_y", text="Y") - col.prop(strip, "reverse_frames", text="Backwards") - layout.prop(strip, "use_color_balance") - if strip.color_balance: # TODO - need to add this somehow + if strip.use_color_balance and strip.color_balance: # TODO - need to add this somehow row = layout.row() row.active = strip.use_color_balance col = row.column() - col.template_color_wheel(strip.color_balance, "lift", value_slider=False) + col.template_color_wheel(strip.color_balance, "lift", value_slider=False, cubic=True) col.row().prop(strip.color_balance, "lift") col.prop(strip.color_balance, "inverse_lift", text="Inverse") col = row.column() - col.template_color_wheel(strip.color_balance, "gamma", value_slider=False) + col.template_color_wheel(strip.color_balance, "gamma", value_slider=False, lock_luminosity=True, cubic=True) col.row().prop(strip.color_balance, "gamma") col.prop(strip.color_balance, "inverse_gamma", text="Inverse") col = row.column() - col.template_color_wheel(strip.color_balance, "gain", value_slider=False) + col.template_color_wheel(strip.color_balance, "gain", value_slider=False, lock_luminosity=True, cubic=True) col.row().prop(strip.color_balance, "gain") col.prop(strip.color_balance, "inverse_gain", text="Inverse") @@ -681,7 +779,7 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel): if not strip: return False - return strip.type in ('MOVIE', 'IMAGE', 'SCENE', 'META') + return strip.type in ('MOVIE', 'IMAGE', 'SCENE', 'META', 'MULTICAM') def draw_header(self, context): strip = act_strip(context) @@ -695,9 +793,12 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel): flow = layout.column_flow() flow.prop(strip, "proxy_custom_directory") + flow.prop(strip, "proxy_custom_file") if strip.proxy: # TODO - need to add this somehow - flow.prop(strip.proxy, "directory") - flow.prop(strip.proxy, "file") + if strip.proxy_custom_directory and not strip.proxy_custom_file: + flow.prop(strip.proxy, "directory") + if strip.proxy_custom_file: + flow.prop(strip.proxy, "filepath") class SEQUENCER_PT_view(SequencerButtonsPanel_Output): @@ -709,9 +810,12 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output): st = context.space_data col = layout.column() - col.prop(st, "draw_overexposed") # text="Zebra" - col.prop(st, "draw_safe_margin") - + if st.display_mode == 'IMAGE': + col.prop(st, "draw_overexposed") # text="Zebra" + col.prop(st, "draw_safe_margin") + if st.display_mode == 'WAVEFORM': + col.prop(st, "separate_color_preview") + col.prop(st, "proxy_render_size") classes = [ SEQUENCER_HT_header, # header/menu classes @@ -724,9 +828,11 @@ classes = [ SEQUENCER_MT_strip, SEQUENCER_PT_edit, # sequencer panels + SEQUENCER_PT_preview, SEQUENCER_PT_effect, SEQUENCER_PT_input_movie, SEQUENCER_PT_input_image, + SEQUENCER_PT_input_secondary, SEQUENCER_PT_sound, SEQUENCER_PT_scene, SEQUENCER_PT_filter, diff --git a/release/scripts/ui/space_text.py b/release/scripts/ui/space_text.py index 3a789566a61..692f1581bd6 100644 --- a/release/scripts/ui/space_text.py +++ b/release/scripts/ui/space_text.py @@ -54,19 +54,19 @@ class TEXT_HT_header(bpy.types.Header): if text: row = layout.row() row.operator("text.run_script") + + row = layout.row() + row.active = text.name.endswith(".py") row.prop(text, "use_module") row = layout.row() - if text.filename != "": + if text.filepath: if text.dirty: - row.label(text="File: *%s (unsaved)" % text.filename) + row.label(text="File: *%s (unsaved)" % text.filepath) else: - row.label(text="File: %s" % text.filename) + row.label(text="File: %s" % text.filepath) else: - if text.library: - row.label(text="Text: External") - else: - row.label(text="Text: Internal") + row.label(text="Text: External" if text.library else "Text: Internal") class TEXT_PT_properties(bpy.types.Panel): @@ -147,7 +147,7 @@ class TEXT_MT_text(bpy.types.Menu): layout.operator("text.save") layout.operator("text.save_as") - if text.filename != "": + if text.filepath: layout.operator("text.make_internal") layout.column() @@ -177,7 +177,7 @@ class TEXT_MT_templates(bpy.types.Menu): bl_label = "Script Templates" def draw(self, context): - self.path_menu(bpy.utils.script_paths("templates"), "text.open") + self.path_menu(bpy.utils.script_paths("templates"), "text.open", {"internal": True}) class TEXT_MT_edit_view(bpy.types.Menu): @@ -274,6 +274,22 @@ class TEXT_MT_edit(bpy.types.Menu): layout.menu("TEXT_MT_edit_to3d") +class TEXT_MT_toolbox(bpy.types.Menu): + bl_label = "" + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_DEFAULT' + + layout.operator("text.cut") + layout.operator("text.copy") + layout.operator("text.paste") + + layout.separator() + + layout.operator("text.run_script") + + classes = [ TEXT_HT_header, TEXT_PT_properties, @@ -285,7 +301,8 @@ classes = [ TEXT_MT_edit_view, TEXT_MT_edit_select, TEXT_MT_edit_markers, - TEXT_MT_edit_to3d] + TEXT_MT_edit_to3d, + TEXT_MT_toolbox] def register(): diff --git a/release/scripts/ui/space_time.py b/release/scripts/ui/space_time.py index 2f2fad59e9f..042e4dbc015 100644 --- a/release/scripts/ui/space_time.py +++ b/release/scripts/ui/space_time.py @@ -39,17 +39,17 @@ class TIME_HT_header(bpy.types.Header): sub.menu("TIME_MT_frame") sub.menu("TIME_MT_playback") - layout.prop(scene, "use_preview_range", text="PR") + layout.prop(scene, "use_preview_range", text="", toggle=True) row = layout.row(align=True) if not scene.use_preview_range: - row.prop(scene, "start_frame", text="Start") - row.prop(scene, "end_frame", text="End") + row.prop(scene, "frame_start", text="Start") + row.prop(scene, "frame_end", text="End") else: - row.prop(scene, "preview_range_start_frame", text="Start") - row.prop(scene, "preview_range_end_frame", text="End") + row.prop(scene, "preview_range_frame_start", text="Start") + row.prop(scene, "preview_range_frame_end", text="End") - layout.prop(scene, "current_frame", text="") + layout.prop(scene, "frame_current", text="") layout.separator() @@ -67,7 +67,7 @@ class TIME_HT_header(bpy.types.Header): row.operator("screen.frame_jump", text="", icon='FF').end = True row = layout.row(align=True) - row.prop(tools, "use_auto_keying", text="", toggle=True, icon='REC') + row.prop(tools, "use_auto_keying", text="", toggle=True) if screen.animation_playing and tools.use_auto_keying: subsub = row.row() subsub.prop(tools, "record_with_nla", toggle=True) @@ -77,7 +77,7 @@ class TIME_HT_header(bpy.types.Header): layout.separator() row = layout.row(align=True) - row.prop_object(scene, "active_keying_set", scene, "keying_sets", text="") + row.prop_object(scene, "active_keying_set", scene, "all_keying_sets", text="") row.operator("anim.keyframe_insert", text="", icon='KEY_HLT') row.operator("anim.keyframe_delete", text="", icon='KEY_DEHLT') @@ -100,9 +100,33 @@ class TIME_MT_view(bpy.types.Menu): layout.separator() + layout.menu("TIME_MT_cache") + + layout.separator() + layout.operator("marker.camera_bind") +class TIME_MT_cache(bpy.types.Menu): + bl_label = "Cache" + + def draw(self, context): + layout = self.layout + + st = context.space_data + + layout.prop(st, "show_cache") + + layout.separator() + + col = layout.column() + col.enabled = st.show_cache + col.prop(st, "cache_softbody") + col.prop(st, "cache_particles") + col.prop(st, "cache_cloth") + col.prop(st, "cache_smoke") + + class TIME_MT_frame(bpy.types.Menu): bl_label = "Frame" @@ -171,6 +195,7 @@ class TIME_MT_autokey(bpy.types.Menu): classes = [ TIME_HT_header, TIME_MT_view, + TIME_MT_cache, TIME_MT_frame, TIME_MT_autokey, TIME_MT_playback] diff --git a/release/scripts/ui/space_userpref.py b/release/scripts/ui/space_userpref.py index bea9f80edec..61f759a1d83 100644 --- a/release/scripts/ui/space_userpref.py +++ b/release/scripts/ui/space_userpref.py @@ -19,26 +19,34 @@ # <pep8 compliant> import bpy import os -import re import shutil def ui_items_general(col, context): """ General UI Theme Settings (User Interface) """ + row = col.row() - sub = row.column() - sub.prop(context, "outline") - sub.prop(context, "item", slider=True) - sub = row.column() - sub.prop(context, "inner", slider=True) - sub.prop(context, "inner_sel", slider=True) - sub = row.column() - sub.prop(context, "text") - sub.prop(context, "text_sel") - sub = row.column() - sub.prop(context, "shaded") - subsub = sub.column(align=True) + + subsplit = row.split(percentage=0.95) + + padding = subsplit.split(percentage=0.15) + colsub = padding.column() + colsub = padding.column() + colsub.row().prop(context, "outline") + colsub.row().prop(context, "item", slider=True) + colsub.row().prop(context, "inner", slider=True) + colsub.row().prop(context, "inner_sel", slider=True) + + subsplit = row.split(percentage=0.85) + + padding = subsplit.split(percentage=0.15) + colsub = padding.column() + colsub = padding.column() + colsub.row().prop(context, "text") + colsub.row().prop(context, "text_sel") + colsub.prop(context, "shaded") + subsub = colsub.column(align=True) subsub.active = context.shaded subsub.prop(context, "shadetop") subsub.prop(context, "shadedown") @@ -67,91 +75,6 @@ def opengl_lamp_buttons(column, lamp): col.active = lamp.enabled col.prop(lamp, "direction", text="") -KM_HIERARCHY = [ - ('Window', 'EMPTY', 'WINDOW', []), # file save, window change, exit - ('Screen', 'EMPTY', 'WINDOW', [ # full screen, undo, screenshot - ('Screen Editing', 'EMPTY', 'WINDOW', []), # resizing, action corners - ]), - - ('View2D', 'EMPTY', 'WINDOW', []), # view 2d navigation (per region) - ('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation - ('Header', 'EMPTY', 'WINDOW', []), # header stuff (per region) - ('Grease Pencil', 'EMPTY', 'WINDOW', []), # grease pencil stuff (per region) - - ('3D View', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform) - ('Object Mode', 'EMPTY', 'WINDOW', []), - ('Mesh', 'EMPTY', 'WINDOW', []), - ('Curve', 'EMPTY', 'WINDOW', []), - ('Armature', 'EMPTY', 'WINDOW', []), - ('Metaball', 'EMPTY', 'WINDOW', []), - ('Lattice', 'EMPTY', 'WINDOW', []), - ('Font', 'EMPTY', 'WINDOW', []), - - ('Pose', 'EMPTY', 'WINDOW', []), - - ('Vertex Paint', 'EMPTY', 'WINDOW', []), - ('Weight Paint', 'EMPTY', 'WINDOW', []), - ('Face Mask', 'EMPTY', 'WINDOW', []), - ('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d - ('Sculpt', 'EMPTY', 'WINDOW', []), - - ('Armature Sketch', 'EMPTY', 'WINDOW', []), - ('Particle', 'EMPTY', 'WINDOW', []), - - ('Object Non-modal', 'EMPTY', 'WINDOW', []), # mode change - - ('3D View Generic', 'VIEW_3D', 'WINDOW', []) # toolbar and properties - ]), - - ('Frames', 'EMPTY', 'WINDOW', []), # frame navigation (per region) - ('Markers', 'EMPTY', 'WINDOW', []), # markers (per region) - ('Animation', 'EMPTY', 'WINDOW', []), # frame change on click, preview range (per region) - ('Animation Channels', 'EMPTY', 'WINDOW', []), - ('Graph Editor', 'GRAPH_EDITOR', 'WINDOW', [ - ('Graph Editor Generic', 'GRAPH_EDITOR', 'WINDOW', []) - ]), - ('Dopesheet', 'DOPESHEET_EDITOR', 'WINDOW', []), - ('NLA Editor', 'NLA_EDITOR', 'WINDOW', [ - ('NLA Channels', 'NLA_EDITOR', 'WINDOW', []), - ('NLA Generic', 'NLA_EDITOR', 'WINDOW', []) - ]), - - ('Image', 'IMAGE_EDITOR', 'WINDOW', [ - ('UV Editor', 'EMPTY', 'WINDOW', []), # image (reverse order, UVEdit before Image - ('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d - ('Image Generic', 'IMAGE_EDITOR', 'WINDOW', []) - ]), - - ('Timeline', 'TIMELINE', 'WINDOW', []), - ('Outliner', 'OUTLINER', 'WINDOW', []), - - ('Node Editor', 'NODE_EDITOR', 'WINDOW', [ - ('Node Generic', 'NODE_EDITOR', 'WINDOW', []) - ]), - ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', []), - ('Logic Editor', 'LOGIC_EDITOR', 'WINDOW', []), - - ('File Browser', 'FILE_BROWSER', 'WINDOW', [ - ('File Browser Main', 'FILE_BROWSER', 'WINDOW', []), - ('File Browser Buttons', 'FILE_BROWSER', 'WINDOW', []) - ]), - - ('Property Editor', 'PROPERTIES', 'WINDOW', []), # align context menu - - ('Script', 'SCRIPTS_WINDOW', 'WINDOW', []), - ('Text', 'TEXT_EDITOR', 'WINDOW', []), - ('Console', 'CONSOLE', 'WINDOW', []), - - ('View3D Gesture Circle', 'EMPTY', 'WINDOW', []), - ('Gesture Border', 'EMPTY', 'WINDOW', []), - ('Standard Modal Map', 'EMPTY', 'WINDOW', []), - ('Transform Modal Map', 'EMPTY', 'WINDOW', []), - ('View3D Fly Modal', 'EMPTY', 'WINDOW', []), - ('View3D Rotate Modal', 'EMPTY', 'WINDOW', []), - ('View3D Move Modal', 'EMPTY', 'WINDOW', []), - ('View3D Zoom Modal', 'EMPTY', 'WINDOW', []), - ] - class USERPREF_HT_header(bpy.types.Header): bl_space_type = 'USER_PREFERENCES' @@ -165,16 +88,18 @@ class USERPREF_HT_header(bpy.types.Header): layout.operator_context = 'EXEC_AREA' layout.operator("wm.save_homefile", text="Save As Default") + layout.operator_context = 'INVOKE_DEFAULT' + if userpref.active_section == 'INPUT': - layout.operator_context = 'INVOKE_DEFAULT' - op = layout.operator("wm.keyconfig_export", "Export Key Configuration...") - op.path = "keymap.py" - op = layout.operator("wm.keyconfig_import", "Import Key Configuration...") - op.path = "keymap.py" + op = layout.operator("wm.keyconfig_export") + op.filepath = "keymap.py" + op = layout.operator("wm.keyconfig_import") + op.filepath = "keymap.py" elif userpref.active_section == 'ADDONS': - layout.operator_context = 'INVOKE_DEFAULT' - op = layout.operator("wm.addon_install", "Install Add-On...") - op.path = "*.py" + op = layout.operator("wm.addon_install") + op.filepath = "*.py" + elif userpref.active_section == 'THEMES': + op = layout.operator("ui.reset_default_theme") class USERPREF_PT_tabs(bpy.types.Panel): @@ -191,6 +116,26 @@ class USERPREF_PT_tabs(bpy.types.Panel): layout.prop(userpref, "active_section", expand=True) +class USERPREF_MT_interaction_presets(bpy.types.Menu): + bl_label = "Presets" + preset_subdir = "interaction" + preset_operator = "script.execute_preset" + draw = bpy.types.Menu.draw_preset + + +class USERPREF_MT_splash(bpy.types.Menu): + bl_label = "Splash" + + def draw(self, context): + layout = self.layout + split = layout.split() + row = split.row() + row.label("") + row = split.row() + row.label("Interaction:") + row.menu("USERPREF_MT_interaction_presets", text=bpy.types.USERPREF_MT_interaction_presets.bl_label) + + class USERPREF_PT_interface(bpy.types.Panel): bl_space_type = 'USER_PREFERENCES' bl_label = "Interface" @@ -217,7 +162,6 @@ class USERPREF_PT_interface(bpy.types.Panel): col.prop(view, "show_view_name", text="View Name") col.prop(view, "show_playback_fps", text="Playback FPS") col.prop(view, "global_scene") - col.prop(view, "pin_floating_panels") col.prop(view, "object_origin_size") col.separator() @@ -230,6 +174,13 @@ class USERPREF_PT_interface(bpy.types.Panel): sub.prop(view, "mini_axis_size", text="Size") sub.prop(view, "mini_axis_brightness", text="Brightness") + col.separator() + col.separator() + col.separator() + + col.label(text="Properties Window:") + col.prop(view, "properties_width_check") + row.separator() row.separator() @@ -280,6 +231,10 @@ class USERPREF_PT_interface(bpy.types.Panel): col.prop(view, "open_toplevel_delay", text="Top Level") col.prop(view, "open_sublevel_delay", text="Sub Level") + col.separator() + + col.prop(view, "show_splash") + class USERPREF_PT_edit(bpy.types.Panel): bl_space_type = 'USER_PREFERENCES' @@ -325,13 +280,6 @@ class USERPREF_PT_edit(bpy.types.Panel): row.separator() col = row.column() - col.label(text="Snap:") - col.prop(edit, "snap_translate", text="Translate") - col.prop(edit, "snap_rotate", text="Rotate") - col.prop(edit, "snap_scale", text="Scale") - col.separator() - col.separator() - col.separator() col.label(text="Grease Pencil:") col.prop(edit, "grease_pencil_manhattan_distance", text="Manhattan Distance") col.prop(edit, "grease_pencil_euclidean_distance", text="Euclidean Distance") @@ -365,7 +313,8 @@ class USERPREF_PT_edit(bpy.types.Panel): col.separator() col.label(text="New F-Curve Defaults:") - col.prop(edit, "new_interpolation_type", text="Interpolation") + col.prop(edit, "keyframe_new_interpolation_type", text="Interpolation") + col.prop(edit, "keyframe_new_handle_type", text="Handles") col.prop(edit, "insertkey_xyz_to_rgb", text="XYZ to RGB") col.separator() @@ -378,7 +327,21 @@ class USERPREF_PT_edit(bpy.types.Panel): row.separator() row.separator() + sculpt = context.tool_settings.sculpt col = row.column() + col.label(text="Paint and Sculpt:") + col.prop(edit, "sculpt_paint_use_unified_size", text="Unify Size") + col.prop(edit, "sculpt_paint_use_unified_strength", text="Unify Strength") + row = col.row(align=True) + row.label("Overlay Color:") + row.prop(edit, "sculpt_paint_overlay_col", text="") + col.prop(sculpt, "use_openmp", text="Threaded Sculpt") + col.prop(sculpt, "show_brush") + + col.separator() + col.separator() + col.separator() + col.label(text="Duplicate Data:") col.prop(edit, "duplicate_mesh", text="Mesh") col.prop(edit, "duplicate_surface", text="Surface") @@ -423,6 +386,7 @@ class USERPREF_PT_system(bpy.types.Panel): col.prop(system, "frame_server_port") col.prop(system, "scrollback", text="Console Scrollback") col.prop(system, "auto_execute_scripts") + col.prop(system, "tabs_as_spaces") col.separator() col.separator() @@ -533,6 +497,36 @@ class USERPREF_PT_theme(bpy.types.Panel): bl_region_type = 'WINDOW' bl_show_header = False + @staticmethod + def _theme_generic(split, themedata): + + row = split.row() + + subsplit = row.split(percentage=0.95) + + padding1 = subsplit.split(percentage=0.15) + padding1.column() + + subsplit = row.split(percentage=0.85) + + padding2 = subsplit.split(percentage=0.15) + padding2.column() + + colsub_pair = padding1.column(), padding2.column() + + props_type = {} + + for i, prop in enumerate(themedata.rna_type.properties): + attr = prop.identifier + if attr == "rna_type": + continue + + props_type.setdefault((prop.type, prop.subtype), []).append(prop.identifier) + + for props_type, props_ls in sorted(props_type.items()): + for i, attr in enumerate(props_ls): + colsub_pair[i % 2].row().prop(themedata, attr) + def poll(self, context): userpref = context.user_preferences return (userpref.active_section == 'THEMES') @@ -545,6 +539,12 @@ class USERPREF_PT_theme(bpy.types.Panel): split_themes = layout.split(percentage=0.2) split_themes.prop(theme, "theme_area", expand=True) + split = layout.split(percentage=0.4) + + + layout.separator() + layout.separator() + split = split_themes.split() if theme.theme_area == 'USER_INTERFACE': @@ -606,6 +606,10 @@ class USERPREF_PT_theme(bpy.types.Panel): col.label(text="Scroll Bar:") ui_items_general(col, ui) + ui = theme.user_interface.wcol_progress + col.label(text="Progress Bar:") + ui_items_general(col, ui) + ui = theme.user_interface.wcol_list_item col.label(text="List Item:") ui_items_general(col, ui) @@ -614,360 +618,39 @@ class USERPREF_PT_theme(bpy.types.Panel): col.label(text="State:") row = col.row() - sub = row.column() - sub.prop(ui, "inner_anim") - sub.prop(ui, "inner_anim_sel") - sub = row.column() - sub.prop(ui, "inner_driven") - sub.prop(ui, "inner_driven_sel") - sub = row.column() - sub.prop(ui, "inner_key") - sub.prop(ui, "inner_key_sel") - sub = row.column() - sub.prop(ui, "blend") - ui = theme.user_interface - col.separator() - col.separator() - col.prop(ui, "icon_file") + subsplit = row.split(percentage=0.95) - layout.separator() - layout.separator() + padding = subsplit.split(percentage=0.15) + colsub = padding.column() + colsub = padding.column() + colsub.row().prop(ui, "inner_anim") + colsub.row().prop(ui, "inner_anim_sel") + colsub.row().prop(ui, "inner_driven") + colsub.row().prop(ui, "inner_driven_sel") + subsplit = row.split(percentage=0.85) - elif theme.theme_area == 'VIEW_3D': - v3d = theme.view_3d + padding = subsplit.split(percentage=0.15) + colsub = padding.column() + colsub = padding.column() + colsub.row().prop(ui, "inner_key") + colsub.row().prop(ui, "inner_key_sel") + colsub.row().prop(ui, "blend") - col = split.column() - col.prop(v3d, "back") - col.prop(v3d, "button") - col.prop(v3d, "button_title") - col.prop(v3d, "button_text") - col.prop(v3d, "header") - col = split.column() - col.prop(v3d, "grid") - col.prop(v3d, "wire") - col.prop(v3d, "lamp", slider=True) - col.prop(v3d, "editmesh_active", slider=True) - - col = split.column() - col.prop(v3d, "object_selected") - col.prop(v3d, "object_active") - col.prop(v3d, "object_grouped") - col.prop(v3d, "object_grouped_active") - col.prop(v3d, "transform") - - col = split.column() - col.prop(v3d, "vertex") - col.prop(v3d, "face", slider=True) - col.prop(v3d, "normal") - col.prop(v3d, "vertex_normal") - col.prop(v3d, "bone_solid") - col.prop(v3d, "bone_pose") - #col.prop(v3d, "edge") Doesn't seem to work - - elif theme.theme_area == 'GRAPH_EDITOR': - graph = theme.graph_editor - - col = split.column() - col.prop(graph, "back") - col.prop(graph, "button") - col.prop(graph, "button_title") - col.prop(graph, "button_text") - - col = split.column() - col.prop(graph, "header") - col.prop(graph, "grid") - col.prop(graph, "list") - col.prop(graph, "channel_group") - - col = split.column() - col.prop(graph, "active_channels_group") - col.prop(graph, "dopesheet_channel") - col.prop(graph, "dopesheet_subchannel") - col.prop(graph, "vertex") - - col = split.column() - col.prop(graph, "current_frame") - col.prop(graph, "handle_vertex") - col.prop(graph, "handle_vertex_select") + ui = theme.user_interface + col.separator() col.separator() - col.prop(graph, "handle_vertex_size") - - elif theme.theme_area == 'FILE_BROWSER': - file_browse = theme.file_browser - - col = split.column() - col.prop(file_browse, "back") - col.prop(file_browse, "text") - col.prop(file_browse, "text_hi") - - col = split.column() - col.prop(file_browse, "header") - col.prop(file_browse, "list") - - col = split.column() - col.prop(file_browse, "selected_file") - col.prop(file_browse, "tiles") - - col = split.column() - col.prop(file_browse, "active_file") - col.prop(file_browse, "active_file_text") - - elif theme.theme_area == 'NLA_EDITOR': - nla = theme.nla_editor - - col = split.column() - col.prop(nla, "back") - col.prop(nla, "button") - col.prop(nla, "button_title") - - col = split.column() - col.prop(nla, "button_text") - col.prop(nla, "text") - col.prop(nla, "header") - - col = split.column() - col.prop(nla, "grid") - col.prop(nla, "bars") - col.prop(nla, "bars_selected") - - col = split.column() - col.prop(nla, "strips") - col.prop(nla, "strips_selected") - col.prop(nla, "current_frame") - - elif theme.theme_area == 'DOPESHEET_EDITOR': - dope = theme.dopesheet_editor - - col = split.column() - col.prop(dope, "back") - col.prop(dope, "list") - col.prop(dope, "text") - col.prop(dope, "header") - - col = split.column() - col.prop(dope, "grid") - col.prop(dope, "channels") - col.prop(dope, "channels_selected") - col.prop(dope, "channel_group") - - col = split.column() - col.prop(dope, "active_channels_group") - col.prop(dope, "long_key") - col.prop(dope, "long_key_selected") - - col = split.column() - col.prop(dope, "current_frame") - col.prop(dope, "dopesheet_channel") - col.prop(dope, "dopesheet_subchannel") - - elif theme.theme_area == 'IMAGE_EDITOR': - image = theme.image_editor - - col = split.column() - col.prop(image, "back") - col.prop(image, "scope_back") - col.prop(image, "button") - - col = split.column() - col.prop(image, "button_title") - col.prop(image, "button_text") - - col = split.column() - col.prop(image, "header") - - col = split.column() - col.prop(image, "editmesh_active", slider=True) - - elif theme.theme_area == 'SEQUENCE_EDITOR': - seq = theme.sequence_editor - - col = split.column() - col.prop(seq, "back") - col.prop(seq, "button") - col.prop(seq, "button_title") - col.prop(seq, "button_text") - col.prop(seq, "text") - - col = split.column() - col.prop(seq, "header") - col.prop(seq, "grid") - col.prop(seq, "movie_strip") - col.prop(seq, "image_strip") - col.prop(seq, "scene_strip") - - col = split.column() - col.prop(seq, "audio_strip") - col.prop(seq, "effect_strip") - col.prop(seq, "plugin_strip") - col.prop(seq, "transition_strip") - - col = split.column() - col.prop(seq, "meta_strip") - col.prop(seq, "current_frame") - col.prop(seq, "keyframe") - col.prop(seq, "draw_action") - - elif theme.theme_area == 'PROPERTIES': - prop = theme.properties - - col = split.column() - col.prop(prop, "back") - - col = split.column() - col.prop(prop, "title") - - col = split.column() - col.prop(prop, "text") - - col = split.column() - col.prop(prop, "header") - - elif theme.theme_area == 'TEXT_EDITOR': - text = theme.text_editor - - col = split.column() - col.prop(text, "back") - col.prop(text, "button") - col.prop(text, "button_title") - col.prop(text, "button_text") - - col = split.column() - col.prop(text, "text") - col.prop(text, "text_hi") - col.prop(text, "header") - col.prop(text, "line_numbers_background") - - col = split.column() - col.prop(text, "selected_text") - col.prop(text, "cursor") - col.prop(text, "syntax_builtin") - col.prop(text, "syntax_special") - - col = split.column() - col.prop(text, "syntax_comment") - col.prop(text, "syntax_string") - col.prop(text, "syntax_numbers") - - elif theme.theme_area == 'TIMELINE': - time = theme.timeline - - col = split.column() - col.prop(time, "back") - col.prop(time, "text") - - col = split.column() - col.prop(time, "header") - - col = split.column() - col.prop(time, "grid") - - col = split.column() - col.prop(time, "current_frame") - - elif theme.theme_area == 'NODE_EDITOR': - node = theme.node_editor - - col = split.column() - col.prop(node, "back") - col.prop(node, "button") - col.prop(node, "button_title") - col.prop(node, "button_text") - - col = split.column() - col.prop(node, "text") - col.prop(node, "text_hi") - col.prop(node, "header") - col.prop(node, "wires") - - col = split.column() - col.prop(node, "wire_select") - col.prop(node, "selected_text") - col.prop(node, "node_backdrop", slider=True) - col.prop(node, "in_out_node") - - col = split.column() - col.prop(node, "converter_node") - col.prop(node, "operator_node") - col.prop(node, "group_node") - - elif theme.theme_area == 'LOGIC_EDITOR': - logic = theme.logic_editor - - col = split.column() - col.prop(logic, "back") - col.prop(logic, "button") - - col = split.column() - col.prop(logic, "button_title") - col.prop(logic, "button_text") - - col = split.column() - col.prop(logic, "text") - col.prop(logic, "header") - - col = split.column() - col.prop(logic, "panel") - - elif theme.theme_area == 'OUTLINER': - out = theme.outliner - - col = split.column() - col.prop(out, "back") - - col = split.column() - col.prop(out, "text") - - col = split.column() - col.prop(out, "text_hi") - - col = split.column() - col.prop(out, "header") - - elif theme.theme_area == 'INFO': - info = theme.info - - col = split.column() - col.prop(info, "back") - - col = split.column() - col.prop(info, "header") - - col = split.column() - col.prop(info, "header_text") - - col = split.column() - - elif theme.theme_area == 'USER_PREFERENCES': - prefs = theme.user_preferences - - col = split.column() - col.prop(prefs, "back") - - col = split.column() - col.prop(prefs, "text") - - col = split.column() - col.prop(prefs, "header") - - col = split.column() - col.prop(prefs, "header_text") - elif theme.theme_area == 'CONSOLE': - prefs = theme.console + split = col.split(percentage=0.93) + split.prop(ui, "icon_file") - col = split.column() - col.prop(prefs, "back") - col.prop(prefs, "header") + layout.separator() + layout.separator() - col = split.column() - col.prop(prefs, "line_output") - col.prop(prefs, "line_input") - col.prop(prefs, "line_info") - col.prop(prefs, "line_error") - col.prop(prefs, "cursor") + else: + self._theme_generic(split, getattr(theme, theme.theme_area.lower())) class USERPREF_PT_file(bpy.types.Panel): @@ -1003,6 +686,7 @@ class USERPREF_PT_file(bpy.types.Panel): sub.label(text="Scripts:") sub.label(text="Sounds:") sub.label(text="Temp:") + sub.label(text="Image Editor:") sub.label(text="Animation Player:") sub = col1.column() @@ -1014,6 +698,7 @@ class USERPREF_PT_file(bpy.types.Panel): sub.prop(paths, "python_scripts_directory", text="") sub.prop(paths, "sounds_directory", text="") sub.prop(paths, "temporary_directory", text="") + sub.prop(paths, "image_editor", text="") subsplit = sub.split(percentage=0.3) subsplit.prop(paths, "animation_player_preset", text="") subsplit.prop(paths, "animation_player", text="") @@ -1038,202 +723,29 @@ class USERPREF_PT_file(bpy.types.Panel): sub.enabled = paths.auto_save_temporary_files sub.prop(paths, "auto_save_time", text="Timer (mins)") +from space_userpref_keymap import InputKeyMapPanel + -class USERPREF_PT_input(bpy.types.Panel): +class USERPREF_PT_input(InputKeyMapPanel): bl_space_type = 'USER_PREFERENCES' bl_label = "Input" - bl_region_type = 'WINDOW' - bl_show_header = False def poll(self, context): userpref = context.user_preferences return (userpref.active_section == 'INPUT') - def draw_entry(self, kc, entry, col, level=0): - idname, spaceid, regionid, children = entry - - km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid) - - if km: - self.draw_km(kc, km, children, col, level) - - def indented_layout(self, layout, level): - indentpx = 16 - if level == 0: - level = 0.0001 # Tweak so that a percentage of 0 won't split by half - indent = level * indentpx / bpy.context.region.width - - split = layout.split(percentage=indent) - col = split.column() - col = split.column() - return col - - def draw_km(self, kc, km, children, layout, level): - km = km.active() - - layout.set_context_pointer("keymap", km) - - col = self.indented_layout(layout, level) - - row = col.row() - row.prop(km, "children_expanded", text="", no_bg=True) - row.label(text=km.name) - - row.label() - row.label() - - if km.modal: - row.label(text="", icon='LINKED') - if km.user_defined: - op = row.operator("wm.keymap_restore", text="Restore") - else: - op = row.operator("wm.keymap_edit", text="Edit") - - if km.children_expanded: - if children: - # Put the Parent key map's entries in a 'global' sub-category - # equal in hierarchy to the other children categories - subcol = self.indented_layout(col, level + 1) - subrow = subcol.row() - subrow.prop(km, "items_expanded", text="", no_bg=True) - subrow.label(text="%s (Global)" % km.name) - else: - km.items_expanded = True - - # Key Map items - if km.items_expanded: - for kmi in km.items: - self.draw_kmi(kc, km, kmi, col, level + 1) - - # "Add New" at end of keymap item list - col = self.indented_layout(col, level + 1) - subcol = col.split(percentage=0.2).column() - subcol.active = km.user_defined - op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') - - col.separator() - - # Child key maps - if children: - subcol = col.column() - row = subcol.row() - - for entry in children: - self.draw_entry(kc, entry, col, level + 1) - - def draw_kmi(self, kc, km, kmi, layout, level): - map_type = kmi.map_type - - col = self.indented_layout(layout, level) - - if km.user_defined: - col = col.column(align=True) - box = col.box() - else: - box = col.column() - - split = box.split(percentage=0.05) - - # header bar - row = split.row() - row.prop(kmi, "expanded", text="", no_bg=True) - - row = split.row() - row.enabled = km.user_defined - row.prop(kmi, "active", text="", no_bg=True) - - if km.modal: - row.prop(kmi, "propvalue", text="") - else: - row.label(text=kmi.name) - - row = split.row() - row.enabled = km.user_defined - row.prop(kmi, "map_type", text="") - if map_type == 'KEYBOARD': - row.prop(kmi, "type", text="", full_event=True) - elif map_type == 'MOUSE': - row.prop(kmi, "type", text="", full_event=True) - elif map_type == 'TWEAK': - subrow = row.row() - subrow.prop(kmi, "type", text="") - subrow.prop(kmi, "value", text="") - elif map_type == 'TIMER': - row.prop(kmi, "type", text="") - else: - row.label() - - if kmi.id: - op = row.operator("wm.keyitem_restore", text="", icon='BACK') - op.item_id = kmi.id - op = row.operator("wm.keyitem_remove", text="", icon='X') - op.item_id = kmi.id - - # Expanded, additional event settings - if kmi.expanded: - box = col.box() - - box.enabled = km.user_defined - - if map_type not in ('TEXTINPUT', 'TIMER'): - split = box.split(percentage=0.4) - sub = split.row() - - if km.modal: - sub.prop(kmi, "propvalue", text="") - else: - sub.prop(kmi, "idname", text="") - - sub = split.column() - subrow = sub.row(align=True) - - if map_type == 'KEYBOARD': - subrow.prop(kmi, "type", text="", event=True) - subrow.prop(kmi, "value", text="") - elif map_type == 'MOUSE': - subrow.prop(kmi, "type", text="") - subrow.prop(kmi, "value", text="") - - subrow = sub.row() - subrow.scale_x = 0.75 - subrow.prop(kmi, "any") - subrow.prop(kmi, "shift") - subrow.prop(kmi, "ctrl") - subrow.prop(kmi, "alt") - subrow.prop(kmi, "oskey", text="Cmd") - subrow.prop(kmi, "key_modifier", text="", event=True) - - def display_properties(properties, title=None): - box.separator() - if title: - box.label(text=title) - flow = box.column_flow(columns=2) - for pname in dir(properties): - if not properties.is_property_hidden(pname): - value = eval("properties." + pname) - if isinstance(value, bpy.types.OperatorProperties): - display_properties(value, title=pname) - else: - flow.prop(properties, pname) - - # Operator properties - props = kmi.properties - if props is not None: - display_properties(props) - - # Modal key maps attached to this operator - if not km.modal: - kmm = kc.find_keymap_modal(kmi.idname) - if kmm: - self.draw_km(kc, kmm, None, layout, level + 1) - layout.set_context_pointer("keymap", km) - def draw_input_prefs(self, inputs, layout): # General settings row = layout.row() col = row.column() sub = col.column() + sub.label(text="Presets:") + subrow = sub.row(align=True) + subrow.menu("USERPREF_MT_interaction_presets", text=bpy.types.USERPREF_MT_interaction_presets.bl_label) + subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMIN') + sub.separator() + sub.label(text="Mouse:") sub1 = sub.column() sub1.enabled = (inputs.select_mouse == 'RIGHT') @@ -1257,8 +769,8 @@ class USERPREF_PT_input(bpy.types.Panel): sub.row().prop(inputs, "view_rotation", expand=True) sub.label(text="Zoom Style:") - sub.row().prop(inputs, "viewport_zoom_style", expand=True) - if inputs.viewport_zoom_style == 'DOLLY': + sub.row().prop(inputs, "zoom_style", text="") + if inputs.zoom_style == 'DOLLY': sub.row().prop(inputs, "zoom_axis", expand=True) sub.prop(inputs, "invert_zoom_direction") @@ -1271,50 +783,15 @@ class USERPREF_PT_input(bpy.types.Panel): #sub.prop(view, "wheel_scroll_lines", text="Scroll Lines") col.separator() - + ''' not implemented yet sub = col.column() sub.label(text="NDOF Device:") sub.prop(inputs, "ndof_pan_speed", text="Pan Speed") sub.prop(inputs, "ndof_rotate_speed", text="Orbit Speed") + ''' row.separator() - def draw_filtered(self, kc, layout): - filter = kc.filter.lower() - - for km in kc.keymaps: - km = km.active() - layout.set_context_pointer("keymap", km) - - filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()] - - if len(filtered_items) != 0: - col = layout.column() - - row = col.row() - row.label(text=km.name, icon="DOT") - - row.label() - row.label() - - if km.user_defined: - op = row.operator("wm.keymap_restore", text="Restore") - else: - op = row.operator("wm.keymap_edit", text="Edit") - - for kmi in filtered_items: - self.draw_kmi(kc, km, kmi, col, 1) - - # "Add New" at end of keymap item list - col = self.indented_layout(layout, 1) - subcol = col.split(percentage=0.2).column() - subcol.active = km.user_defined - op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') - - def draw_hierarchy(self, defkc, layout): - for entry in KM_HIERARCHY: - self.draw_entry(defkc, entry, layout) - def draw(self, context): layout = self.layout @@ -1333,28 +810,7 @@ class USERPREF_PT_input(bpy.types.Panel): self.draw_input_prefs(inputs, split) # Keymap Settings - col = split.column() - # kc = wm.active_keyconfig - kc = wm.default_keyconfig - - sub = col.column() - - subsplit = sub.split() - subcol = subsplit.column() - row = subcol.row() - row.prop_object(wm, "active_keyconfig", wm, "keyconfigs", text="Configuration:") - - layout.set_context_pointer("keyconfig", wm.active_keyconfig) - row.operator("wm.keyconfig_remove", text="", icon='X') - - row.prop(kc, "filter", icon="VIEWZOOM") - - col.separator() - - if kc.filter != "": - self.draw_filtered(kc, col) - else: - self.draw_hierarchy(kc, col) + self.draw_keymaps(context, split) #print("runtime", time.time() - start) @@ -1369,7 +825,8 @@ class USERPREF_PT_addons(bpy.types.Panel): userpref = context.user_preferences return (userpref.active_section == 'ADDONS') - def _addon_list(self): + @staticmethod + def _addon_list(): import sys modules = [] loaded_modules = set() @@ -1388,22 +845,142 @@ class USERPREF_PT_addons(bpy.types.Panel): userpref = context.user_preferences used_ext = {ext.module for ext in userpref.addons} - col = layout.column() + # 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("") + + cats = ["All", "Enabled", "Disabled"] + sorted(cats) + + bpy.types.Scene.EnumProperty(items=[(cat, cat, cat + " addons") for cat in cats], + name="Category", attr="addon_filter", description="Filter add-ons by category") + bpy.types.Scene.StringProperty(name="Search", attr="addon_search", + description="Search within the selected filter") - for mod in self._addon_list(): - box = col.box() - row = box.row() - text = mod.__doc__ - if not text: - text = mod.__name__ - row.label(text=text) + split = layout.split(percentage=0.2) + col = split.column() + col.prop(context.scene, "addon_filter", text="Filter", expand=True) + col.prop(context.scene, "addon_search", text="", icon='VIEWZOOM') + + col = split.column() + + filter = context.scene.addon_filter + search = context.scene.addon_search.lower() + + for mod, info in addons: module_name = mod.__name__ - row.operator("wm.addon_disable" if module_name in used_ext else "wm.addon_enable").module = module_name + is_enabled = module_name in used_ext + + # check if add-on should be visible with current filters + if (filter == "All") or \ + (filter == info["category"]) or \ + (filter == "Enabled" and is_enabled) or \ + (filter == "Disabled" and not is_enabled): + + + if search and search not in info["name"].lower(): + if info["author"]: + if search not in info["author"].lower(): + continue + else: + continue + + # Addon UI Code + box = col.column().box() + colsub = box.column() + row = colsub.row() + + row.operator("wm.addon_expand", icon='TRIA_DOWN' if info["expanded"] else 'TRIA_RIGHT', emboss=False).module = module_name + + rowsub = row.row() + rowsub.active = is_enabled + rowsub.label(text=info["name"], icon='ERROR' if info["warning"] else 'BLENDER') + + if is_enabled: + row.operator("wm.addon_disable", icon='CHECKBOX_HLT', text="", emboss=False).module = module_name + else: + row.operator("wm.addon_enable", icon='CHECKBOX_DEHLT', text="", emboss=False).module = module_name + + # Expanded UI (only if additional infos are available) + if info["expanded"]: + if info["description"]: + split = colsub.row().split(percentage=0.15) + split.label(text='Description:') + split.label(text=info["description"]) + if info["location"]: + split = colsub.row().split(percentage=0.15) + split.label(text='Location:') + split.label(text=info["location"]) + if info["author"]: + split = colsub.row().split(percentage=0.15) + split.label(text='Author:') + split.label(text=info["author"]) + if info["version"]: + split = colsub.row().split(percentage=0.15) + split.label(text='Version:') + split.label(text=info["version"]) + if info["warning"]: + 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"]: + 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 info["wiki_url"] and info["tracker_url"]: + split.separator() + else: + split.separator() + 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} + missing_modules = {ext for ext in used_ext if ext not in module_names} + + if missing_modules and filter in ("All", "Enabled"): + col.column().separator() + col.column().label(text="Missing script files") + + module_names = {mod.__name__ for mod, info in addons} + for ext in sorted(missing_modules): + # Addon UI Code + box = col.column().box() + colsub = box.column() + row = colsub.row() + + row.label(text=ext, icon='ERROR') + row.operator("wm.addon_disable").module = ext from bpy.props import * +def addon_info_get(mod, info_basis={"name": "", "author": "", "version": "", "blender": "", "location": "", "description": "", "wiki_url": "", "tracker_url": "", "category": "", "warning": "", "expanded": False}): + addon_info = getattr(mod, "bl_addon_info", {}) + + # avoid re-initializing + if "_init" in addon_info: + return addon_info + + if not addon_info: + mod.bl_addon_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" @@ -1412,16 +989,24 @@ class WM_OT_addon_enable(bpy.types.Operator): module = StringProperty(name="Module", description="Module name of the addon to enable") def execute(self, context): - import traceback - ext = context.user_preferences.addons.new() module_name = self.properties.module - ext.module = module_name try: mod = __import__(module_name) mod.register() except: + import traceback traceback.print_exc() + return {'CANCELLED'} + + ext = context.user_preferences.addons.new() + ext.module = module_name + + # check if add-on is written for current blender version, or raise a warning + info = addon_info_get(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.") return {'FINISHED'} @@ -1459,20 +1044,18 @@ class WM_OT_addon_disable(bpy.types.Operator): class WM_OT_addon_install(bpy.types.Operator): "Install an addon" bl_idname = "wm.addon_install" - bl_label = "Install Add-On" + bl_label = "Install Add-On..." module = StringProperty(name="Module", description="Module name of the addon to disable") - path = StringProperty(name="File Path", description="File path to write file to") - filename = StringProperty(name="File Name", description="Name of the file") - directory = StringProperty(name="Directory", description="Directory of the file") + filepath = StringProperty(name="File Path", description="File path to write file to") filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) def execute(self, context): import traceback import zipfile - pyfile = self.properties.path + pyfile = self.properties.filepath path_addons = bpy.utils.script_paths("addons")[-1] @@ -1483,7 +1066,7 @@ class WM_OT_addon_install(bpy.types.Operator): #extract the file to "addons" file_to_extract.extractall(path_addons) - + except: traceback.print_exc() return {'CANCELLED'} @@ -1518,382 +1101,26 @@ class WM_OT_addon_install(bpy.types.Operator): return {'RUNNING_MODAL'} -class WM_OT_keyconfig_test(bpy.types.Operator): - "Test keyconfig for conflicts" - bl_idname = "wm.keyconfig_test" - bl_label = "Test Key Configuration for Conflicts" - - def testEntry(self, kc, entry, src=None, parent=None): - result = False - - def kmistr(kmi): - if km.modal: - s = ["kmi = km.add_modal_item(\'%s\', \'%s\', \'%s\'" % (kmi.propvalue, kmi.type, kmi.value)] - else: - s = ["kmi = km.add_item(\'%s\', \'%s\', \'%s\'" % (kmi.idname, kmi.type, kmi.value)] - - if kmi.any: - s.append(", any=True") - else: - if kmi.shift: - s.append(", shift=True") - if kmi.ctrl: - s.append(", ctrl=True") - if kmi.alt: - s.append(", alt=True") - if kmi.oskey: - s.append(", oskey=True") - if kmi.key_modifier and kmi.key_modifier != 'NONE': - s.append(", key_modifier=\'%s\'" % kmi.key_modifier) - - s.append(")\n") - - def export_properties(prefix, properties): - for pname in dir(properties): - if not properties.is_property_hidden(pname): - value = eval("properties.%s" % pname) - if isinstance(value, bpy.types.OperatorProperties): - export_properties(prefix + "." + pname, value) - elif properties.is_property_set(pname): - value = _string_value(value) - if value != "": - s.append(prefix + ".%s = %s\n" % (pname, value)) - - props = kmi.properties - - if props is not None: - export_properties("kmi.properties", props) - - return "".join(s).strip() - - idname, spaceid, regionid, children = entry - - km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid) - - if km: - km = km.active() - - if src: - for item in km.items: - if src.compare(item): - print("===========") - print(parent.name) - print(kmistr(src)) - print(km.name) - print(kmistr(item)) - result = True - - for child in children: - if self.testEntry(kc, child, src, parent): - result = True - else: - for i in range(len(km.items)): - src = km.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] - if src.compare(item): - print("===========") - print(km.name) - print(kmistr(src)) - print(kmistr(item)) - result = True - - for child in children: - if self.testEntry(kc, child): - result = True - - return result - - def testConfig(self, kc): - result = False - for entry in KM_HIERARCHY: - if self.testEntry(kc, entry): - result = True - return result - - def execute(self, context): - wm = context.manager - kc = wm.default_keyconfig - - if self.testConfig(kc): - print("CONFLICT") - - return {'FINISHED'} - - -def _string_value(value): - if isinstance(value, str) or isinstance(value, bool) or isinstance(value, float) or isinstance(value, int): - result = repr(value) - elif getattr(value, '__len__', False): - repr(list(value)) - else: - print("Export key configuration: can't write ", value) - - return result - - -class WM_OT_keyconfig_import(bpy.types.Operator): - "Import key configuration from a python script" - bl_idname = "wm.keyconfig_import" - bl_label = "Import Key Configuration..." - - path = StringProperty(name="File Path", description="File path to write file to") - filename = StringProperty(name="File Name", description="Name of the file") - directory = StringProperty(name="Directory", description="Directory of the file") - filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) - filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'}) - filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) - - keep_original = BoolProperty(name="Keep original", description="Keep original file after copying to configuration folder", default=True) - - def execute(self, context): - if not self.properties.path: - raise Exception("File path not set") - - f = open(self.properties.path, "r") - if not f: - raise Exception("Could not open file") - - name_pattern = re.compile("^kc = wm.add_keyconfig\('(.*)'\)$") - - for line in f.readlines(): - match = name_pattern.match(line) - - if match: - config_name = match.groups()[0] - - f.close() - - path = os.path.split(os.path.split(__file__)[0])[0] # remove ui/space_userpref.py - path = os.path.join(path, "cfg") - - # create config folder if needed - if not os.path.exists(path): - os.mkdir(path) - - path = os.path.join(path, config_name + ".py") - - if self.properties.keep_original: - shutil.copy(self.properties.path, path) - else: - shutil.move(self.properties.path, path) - - __import__(config_name) - - wm = bpy.context.manager - wm.active_keyconfig = wm.keyconfigs[config_name] - - return {'FINISHED'} - - def invoke(self, context, event): - wm = context.manager - wm.add_fileselect(self) - return {'RUNNING_MODAL'} - - -class WM_OT_keyconfig_export(bpy.types.Operator): - "Export key configuration to a python script" - bl_idname = "wm.keyconfig_export" - bl_label = "Export Key Configuration..." - - path = StringProperty(name="File Path", description="File path to write file to") - filename = StringProperty(name="File Name", description="Name of the file") - directory = StringProperty(name="Directory", description="Directory of the file") - filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) - filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'}) - filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) - - def execute(self, context): - if not self.properties.path: - raise Exception("File path not set") - - f = open(self.properties.path, "w") - if not f: - raise Exception("Could not open file") - - wm = context.manager - kc = wm.active_keyconfig - - if kc.name == 'Blender': - name = os.path.splitext(os.path.basename(self.properties.path))[0] - else: - name = kc.name - - f.write("# Configuration %s\n" % name) - - f.write("import bpy\n\n") - f.write("wm = bpy.context.manager\n") - f.write("kc = wm.add_keyconfig('%s')\n\n" % name) - - for km in kc.keymaps: - km = km.active() - f.write("# Map %s\n" % km.name) - f.write("km = kc.add_keymap('%s', space_type='%s', region_type='%s', modal=%s)\n\n" % (km.name, km.space_type, km.region_type, km.modal)) - for kmi in km.items: - if km.modal: - f.write("kmi = km.add_modal_item('%s', '%s', '%s'" % (kmi.propvalue, kmi.type, kmi.value)) - else: - f.write("kmi = km.add_item('%s', '%s', '%s'" % (kmi.idname, kmi.type, kmi.value)) - if kmi.any: - f.write(", any=True") - else: - if kmi.shift: - f.write(", shift=True") - if kmi.ctrl: - f.write(", ctrl=True") - if kmi.alt: - f.write(", alt=True") - if kmi.oskey: - f.write(", oskey=True") - if kmi.key_modifier and kmi.key_modifier != 'NONE': - f.write(", key_modifier='%s'" % kmi.key_modifier) - f.write(")\n") - - def export_properties(prefix, properties): - for pname in dir(properties): - if not properties.is_property_hidden(pname): - value = eval("properties.%s" % pname) - if isinstance(value, bpy.types.OperatorProperties): - export_properties(prefix + "." + pname, value) - elif properties.is_property_set(pname): - value = _string_value(value) - if value != "": - f.write(prefix + ".%s = %s\n" % (pname, value)) - - props = kmi.properties - - if props is not None: - export_properties("kmi.properties", props) - - f.write("\n") - - f.close() - - return {'FINISHED'} - - def invoke(self, context, event): - wm = context.manager - wm.add_fileselect(self) - return {'RUNNING_MODAL'} - - -class WM_OT_keymap_edit(bpy.types.Operator): - "Edit key map" - bl_idname = "wm.keymap_edit" - bl_label = "Edit Key Map" - - def execute(self, context): - wm = context.manager - km = context.keymap - km.copy_to_user() - return {'FINISHED'} - - -class WM_OT_keymap_restore(bpy.types.Operator): - "Restore key map(s)" - bl_idname = "wm.keymap_restore" - bl_label = "Restore Key Map(s)" - - all = BoolProperty(attr="all", name="All Keymaps", description="Restore all keymaps to default") - - def execute(self, context): - wm = context.manager - - if self.properties.all: - for km in wm.default_keyconfig.keymaps: - km.restore_to_default() - else: - km = context.keymap - km.restore_to_default() - - return {'FINISHED'} - - -class WM_OT_keyitem_restore(bpy.types.Operator): - "Restore key map item" - bl_idname = "wm.keyitem_restore" - bl_label = "Restore Key Map Item" - - item_id = IntProperty(attr="item_id", name="Item Identifier", description="Identifier of the item to remove") - - def execute(self, context): - wm = context.manager - km = context.keymap - kmi = km.item_from_id(self.properties.item_id) - - km.restore_item_to_default(kmi) - - return {'FINISHED'} - - -class WM_OT_keyitem_add(bpy.types.Operator): - "Add key map item" - bl_idname = "wm.keyitem_add" - bl_label = "Add Key Map Item" - - def execute(self, context): - wm = context.manager - km = context.keymap - kc = wm.default_keyconfig - - if km.modal: - km.add_modal_item("", 'A', 'PRESS') # kmi - else: - km.add_item("none", 'A', 'PRESS') # kmi - - # clear filter and expand keymap so we can see the newly added item - if kc.filter != '': - kc.filter = '' - km.items_expanded = True - km.children_expanded = True - - return {'FINISHED'} - - -class WM_OT_keyitem_remove(bpy.types.Operator): - "Remove key map item" - bl_idname = "wm.keyitem_remove" - bl_label = "Remove Key Map Item" - - item_id = IntProperty(attr="item_id", name="Item Identifier", description="Identifier of the item to remove") - - def execute(self, context): - wm = context.manager - km = context.keymap - kmi = km.item_from_id(self.properties.item_id) - km.remove_item(kmi) - return {'FINISHED'} - - -class WM_OT_keyconfig_remove(bpy.types.Operator): - "Remove key config" - bl_idname = "wm.keyconfig_remove" - bl_label = "Remove Key Config" +class WM_OT_addon_expand(bpy.types.Operator): + "Display more information on this add-on" + bl_idname = "wm.addon_expand" + bl_label = "" - def poll(self, context): - wm = context.manager - return wm.active_keyconfig.user_defined + module = StringProperty(name="Module", description="Module name of the addon to expand") def execute(self, context): - wm = context.manager - - keyconfig = wm.active_keyconfig - - module = __import__(keyconfig.name) - - os.remove(module.__file__) - - compiled_path = module.__file__ + "c" # for .pyc + module_name = self.properties.module - if os.path.exists(compiled_path): - os.remove(compiled_path) + # unlikely to fail, module should have alredy been imported + try: + mod = __import__(module_name) + except: + import traceback + traceback.print_exc() + return {'CANCELLED'} - wm.remove_keyconfig(keyconfig) + info = addon_info_get(mod) + info["expanded"] = not info["expanded"] return {'FINISHED'} @@ -1908,19 +1135,13 @@ classes = [ USERPREF_PT_input, USERPREF_PT_addons, + USERPREF_MT_interaction_presets, + USERPREF_MT_splash, + WM_OT_addon_enable, WM_OT_addon_disable, WM_OT_addon_install, - - WM_OT_keyconfig_export, - WM_OT_keyconfig_import, - WM_OT_keyconfig_test, - WM_OT_keyconfig_remove, - WM_OT_keymap_edit, - WM_OT_keymap_restore, - WM_OT_keyitem_add, - WM_OT_keyitem_remove, - WM_OT_keyitem_restore] + WM_OT_addon_expand] def register(): diff --git a/release/scripts/ui/space_userpref_keymap.py b/release/scripts/ui/space_userpref_keymap.py new file mode 100644 index 00000000000..c93b24d5cb2 --- /dev/null +++ b/release/scripts/ui/space_userpref_keymap.py @@ -0,0 +1,812 @@ +# ##### 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 os +import re +import shutil + +KM_HIERARCHY = [ + ('Window', 'EMPTY', 'WINDOW', []), # file save, window change, exit + ('Screen', 'EMPTY', 'WINDOW', [ # full screen, undo, screenshot + ('Screen Editing', 'EMPTY', 'WINDOW', []), # resizing, action corners + ]), + + ('View2D', 'EMPTY', 'WINDOW', []), # view 2d navigation (per region) + ('View2D Buttons List', 'EMPTY', 'WINDOW', []), # view 2d with buttons navigation + ('Header', 'EMPTY', 'WINDOW', []), # header stuff (per region) + ('Grease Pencil', 'EMPTY', 'WINDOW', []), # grease pencil stuff (per region) + + ('3D View', 'VIEW_3D', 'WINDOW', [ # view 3d navigation and generic stuff (select, transform) + ('Object Mode', 'EMPTY', 'WINDOW', []), + ('Mesh', 'EMPTY', 'WINDOW', []), + ('Curve', 'EMPTY', 'WINDOW', []), + ('Armature', 'EMPTY', 'WINDOW', []), + ('Metaball', 'EMPTY', 'WINDOW', []), + ('Lattice', 'EMPTY', 'WINDOW', []), + ('Font', 'EMPTY', 'WINDOW', []), + + ('Pose', 'EMPTY', 'WINDOW', []), + + ('Vertex Paint', 'EMPTY', 'WINDOW', []), + ('Weight Paint', 'EMPTY', 'WINDOW', []), + ('Face Mask', 'EMPTY', 'WINDOW', []), + ('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d + ('Sculpt', 'EMPTY', 'WINDOW', []), + + ('Armature Sketch', 'EMPTY', 'WINDOW', []), + ('Particle', 'EMPTY', 'WINDOW', []), + + ('Object Non-modal', 'EMPTY', 'WINDOW', []), # mode change + + ('3D View Generic', 'VIEW_3D', 'WINDOW', []) # toolbar and properties + ]), + + ('Frames', 'EMPTY', 'WINDOW', []), # frame navigation (per region) + ('Markers', 'EMPTY', 'WINDOW', []), # markers (per region) + ('Animation', 'EMPTY', 'WINDOW', []), # frame change on click, preview range (per region) + ('Animation Channels', 'EMPTY', 'WINDOW', []), + ('Graph Editor', 'GRAPH_EDITOR', 'WINDOW', [ + ('Graph Editor Generic', 'GRAPH_EDITOR', 'WINDOW', []) + ]), + ('Dopesheet', 'DOPESHEET_EDITOR', 'WINDOW', []), + ('NLA Editor', 'NLA_EDITOR', 'WINDOW', [ + ('NLA Channels', 'NLA_EDITOR', 'WINDOW', []), + ('NLA Generic', 'NLA_EDITOR', 'WINDOW', []) + ]), + + ('Image', 'IMAGE_EDITOR', 'WINDOW', [ + ('UV Editor', 'EMPTY', 'WINDOW', []), # image (reverse order, UVEdit before Image + ('Image Paint', 'EMPTY', 'WINDOW', []), # image and view3d + ('Image Generic', 'IMAGE_EDITOR', 'WINDOW', []) + ]), + + ('Timeline', 'TIMELINE', 'WINDOW', []), + ('Outliner', 'OUTLINER', 'WINDOW', []), + + ('Node Editor', 'NODE_EDITOR', 'WINDOW', [ + ('Node Generic', 'NODE_EDITOR', 'WINDOW', []) + ]), + ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', []), + ('Logic Editor', 'LOGIC_EDITOR', 'WINDOW', []), + + ('File Browser', 'FILE_BROWSER', 'WINDOW', [ + ('File Browser Main', 'FILE_BROWSER', 'WINDOW', []), + ('File Browser Buttons', 'FILE_BROWSER', 'WINDOW', []) + ]), + + ('Property Editor', 'PROPERTIES', 'WINDOW', []), # align context menu + + ('Script', 'SCRIPTS_WINDOW', 'WINDOW', []), + ('Text', 'TEXT_EDITOR', 'WINDOW', []), + ('Console', 'CONSOLE', 'WINDOW', []), + + ('View3D Gesture Circle', 'EMPTY', 'WINDOW', []), + ('Gesture Border', 'EMPTY', 'WINDOW', []), + ('Standard Modal Map', 'EMPTY', 'WINDOW', []), + ('Transform Modal Map', 'EMPTY', 'WINDOW', []), + ('View3D Fly Modal', 'EMPTY', 'WINDOW', []), + ('View3D Rotate Modal', 'EMPTY', 'WINDOW', []), + ('View3D Move Modal', 'EMPTY', 'WINDOW', []), + ('View3D Zoom Modal', 'EMPTY', 'WINDOW', []), + ] + + +def _km_exists_in(km, export_keymaps): + for km2, kc in export_keymaps: + if km2.name == km.name: + return True + return False + + +def _merge_keymaps(kc1, kc2): + """ note: kc1 takes priority over kc2 + """ + merged_keymaps = [(km, kc1) for km in kc1.keymaps] + if kc1 != kc2: + merged_keymaps.extend([(km, kc2) for km in kc2.keymaps if not _km_exists_in(km, merged_keymaps)]) + + return merged_keymaps + + +class InputKeyMapPanel(bpy.types.Panel): + bl_space_type = 'USER_PREFERENCES' + bl_label = "Input" + bl_region_type = 'WINDOW' + bl_show_header = False + + def draw_entry(self, display_keymaps, entry, col, level=0): + idname, spaceid, regionid, children = entry + + for km, kc in display_keymaps: + if km.name == idname and km.space_type == spaceid and km.region_type == regionid: + self.draw_km(display_keymaps, kc, km, children, col, level) + + ''' + km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid) + if not km: + kc = defkc + km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid) + + if km: + self.draw_km(kc, km, children, col, level) + ''' + + def indented_layout(self, layout, level): + indentpx = 16 + if level == 0: + level = 0.0001 # Tweak so that a percentage of 0 won't split by half + indent = level * indentpx / bpy.context.region.width + + split = layout.split(percentage=indent) + col = split.column() + col = split.column() + return col + + def draw_km(self, display_keymaps, kc, km, children, layout, level): + km = km.active() + + layout.set_context_pointer("keymap", km) + + col = self.indented_layout(layout, level) + + row = col.row() + row.prop(km, "children_expanded", text="", emboss=False) + row.label(text=km.name) + + row.label() + row.label() + + if km.modal: + row.label(text="", icon='LINKED') + if km.user_defined: + op = row.operator("wm.keymap_restore", text="Restore") + else: + op = row.operator("wm.keymap_edit", text="Edit") + + if km.children_expanded: + if children: + # Put the Parent key map's entries in a 'global' sub-category + # equal in hierarchy to the other children categories + subcol = self.indented_layout(col, level + 1) + subrow = subcol.row() + subrow.prop(km, "items_expanded", text="", emboss=False) + subrow.label(text="%s (Global)" % km.name) + else: + km.items_expanded = True + + # Key Map items + if km.items_expanded: + for kmi in km.items: + self.draw_kmi(display_keymaps, kc, km, kmi, col, level + 1) + + # "Add New" at end of keymap item list + col = self.indented_layout(col, level + 1) + subcol = col.split(percentage=0.2).column() + subcol.enabled = km.user_defined + op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') + + col.separator() + + # Child key maps + if children: + subcol = col.column() + row = subcol.row() + + for entry in children: + self.draw_entry(display_keymaps, entry, col, level + 1) + + def draw_kmi(self, display_keymaps, kc, km, kmi, layout, level): + map_type = kmi.map_type + + col = self.indented_layout(layout, level) + + if km.user_defined: + col = col.column(align=True) + box = col.box() + else: + box = col.column() + + split = box.split(percentage=0.05) + + # header bar + row = split.row() + row.prop(kmi, "expanded", text="", emboss=False) + + row = split.row() + row.enabled = km.user_defined + row.prop(kmi, "active", text="", emboss=False) + + if km.modal: + row.prop(kmi, "propvalue", text="") + else: + row.label(text=kmi.name) + + row = split.row() + row.enabled = km.user_defined + row.prop(kmi, "map_type", text="") + if map_type == 'KEYBOARD': + row.prop(kmi, "type", text="", full_event=True) + elif map_type == 'MOUSE': + row.prop(kmi, "type", text="", full_event=True) + elif map_type == 'TWEAK': + subrow = row.row() + subrow.prop(kmi, "type", text="") + subrow.prop(kmi, "value", text="") + elif map_type == 'TIMER': + row.prop(kmi, "type", text="") + else: + row.label() + + if kmi.id: + op = row.operator("wm.keyitem_restore", text="", icon='BACK') + op.item_id = kmi.id + op = row.operator("wm.keyitem_remove", text="", icon='X') + op.item_id = kmi.id + + # Expanded, additional event settings + if kmi.expanded: + box = col.box() + + box.enabled = km.user_defined + + if map_type not in ('TEXTINPUT', 'TIMER'): + split = box.split(percentage=0.4) + sub = split.row() + + if km.modal: + sub.prop(kmi, "propvalue", text="") + else: + sub.prop(kmi, "idname", text="") + + sub = split.column() + subrow = sub.row(align=True) + + if map_type == 'KEYBOARD': + subrow.prop(kmi, "type", text="", event=True) + subrow.prop(kmi, "value", text="") + elif map_type == 'MOUSE': + subrow.prop(kmi, "type", text="") + subrow.prop(kmi, "value", text="") + + subrow = sub.row() + subrow.scale_x = 0.75 + subrow.prop(kmi, "any") + subrow.prop(kmi, "shift") + subrow.prop(kmi, "ctrl") + subrow.prop(kmi, "alt") + subrow.prop(kmi, "oskey", text="Cmd") + subrow.prop(kmi, "key_modifier", text="", event=True) + + def display_properties(properties, title=None): + box.separator() + if title: + box.label(text=title) + flow = box.column_flow(columns=2) + for pname in dir(properties): + if not properties.is_property_hidden(pname): + value = eval("properties." + pname) + if isinstance(value, bpy.types.OperatorProperties): + display_properties(value, title=pname) + else: + flow.prop(properties, pname) + + # Operator properties + props = kmi.properties + if props is not None: + display_properties(props) + + # Modal key maps attached to this operator + if not km.modal: + kmm = kc.find_keymap_modal(kmi.idname) + if kmm: + self.draw_km(display_keymaps, kc, kmm, None, layout, level + 1) + layout.set_context_pointer("keymap", km) + + def draw_filtered(self, display_keymaps, filter, layout): + for km, kc in display_keymaps: + km = km.active() + layout.set_context_pointer("keymap", km) + + filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()] + + if len(filtered_items) != 0: + col = layout.column() + + row = col.row() + row.label(text=km.name, icon="DOT") + + row.label() + row.label() + + if km.user_defined: + op = row.operator("wm.keymap_restore", text="Restore") + else: + op = row.operator("wm.keymap_edit", text="Edit") + + for kmi in filtered_items: + self.draw_kmi(display_keymaps, kc, km, kmi, col, 1) + + # "Add New" at end of keymap item list + col = self.indented_layout(layout, 1) + subcol = col.split(percentage=0.2).column() + subcol.enabled = km.user_defined + op = subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') + + def draw_hierarchy(self, display_keymaps, layout): + for entry in KM_HIERARCHY: + self.draw_entry(display_keymaps, entry, layout) + + def draw_keymaps(self, context, layout): + wm = context.manager + kc = wm.active_keyconfig + defkc = wm.default_keyconfig + + col = layout.column() + sub = col.column() + + subsplit = sub.split() + subcol = subsplit.column() + + row = subcol.row() + row.prop_object(wm, "active_keyconfig", wm, "keyconfigs", text="Key Config:") + layout.set_context_pointer("keyconfig", wm.active_keyconfig) + row.operator("wm.keyconfig_remove", text="", icon='X') + + row.prop(context.space_data, "filter", icon="VIEWZOOM") + + col.separator() + + display_keymaps = _merge_keymaps(kc, defkc) + if context.space_data.filter != "": + filter = context.space_data.filter.lower() + self.draw_filtered(display_keymaps, filter, col) + else: + self.draw_hierarchy(display_keymaps, col) + + +from bpy.props import * + + +class WM_OT_keyconfig_test(bpy.types.Operator): + "Test keyconfig for conflicts" + bl_idname = "wm.keyconfig_test" + bl_label = "Test Key Configuration for Conflicts" + + def testEntry(self, kc, entry, src=None, parent=None): + result = False + + def kmistr(kmi): + if km.modal: + s = ["kmi = km.items.add_modal(\'%s\', \'%s\', \'%s\'" % (kmi.propvalue, kmi.type, kmi.value)] + else: + s = ["kmi = km.items.add(\'%s\', \'%s\', \'%s\'" % (kmi.idname, kmi.type, kmi.value)] + + if kmi.any: + s.append(", any=True") + else: + if kmi.shift: + s.append(", shift=True") + if kmi.ctrl: + s.append(", ctrl=True") + if kmi.alt: + s.append(", alt=True") + if kmi.oskey: + s.append(", oskey=True") + if kmi.key_modifier and kmi.key_modifier != 'NONE': + s.append(", key_modifier=\'%s\'" % kmi.key_modifier) + + s.append(")\n") + + def export_properties(prefix, properties): + for pname in dir(properties): + if not properties.is_property_hidden(pname): + value = eval("properties.%s" % pname) + if isinstance(value, bpy.types.OperatorProperties): + export_properties(prefix + "." + pname, value) + elif properties.is_property_set(pname): + value = _string_value(value) + if value != "": + s.append(prefix + ".%s = %s\n" % (pname, value)) + + props = kmi.properties + + if props is not None: + export_properties("kmi.properties", props) + + return "".join(s).strip() + + idname, spaceid, regionid, children = entry + + km = kc.find_keymap(idname, space_type=spaceid, region_type=regionid) + + if km: + km = km.active() + + if src: + for item in km.items: + if src.compare(item): + print("===========") + print(parent.name) + print(kmistr(src)) + print(km.name) + print(kmistr(item)) + result = True + + for child in children: + if self.testEntry(kc, child, src, parent): + result = True + else: + for i in range(len(km.items)): + src = km.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] + if src.compare(item): + print("===========") + print(km.name) + print(kmistr(src)) + print(kmistr(item)) + result = True + + for child in children: + if self.testEntry(kc, child): + result = True + + return result + + def testConfig(self, kc): + result = False + for entry in KM_HIERARCHY: + if self.testEntry(kc, entry): + result = True + return result + + def execute(self, context): + wm = context.manager + kc = wm.default_keyconfig + + if self.testConfig(kc): + print("CONFLICT") + + return {'FINISHED'} + + +def _string_value(value): + if isinstance(value, str) or isinstance(value, bool) or isinstance(value, float) or isinstance(value, int): + result = repr(value) + elif getattr(value, '__len__', False): + repr(list(value)) + else: + print("Export key configuration: can't write ", value) + + return result + + +class WM_OT_keyconfig_import(bpy.types.Operator): + "Import key configuration from a python script" + bl_idname = "wm.keyconfig_import" + bl_label = "Import Key Configuration..." + + filepath = StringProperty(name="File Path", description="Filepath to write file to") + filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) + filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'}) + filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) + + keep_original = BoolProperty(name="Keep original", description="Keep original file after copying to configuration folder", default=True) + + def execute(self, context): + if not self.properties.filepath: + raise Exception("Filepath not set") + + f = open(self.properties.filepath, "r") + if not f: + raise Exception("Could not open file") + + name_pattern = re.compile("^kc = wm.add_keyconfig\('(.*)'\)$") + + for line in f.readlines(): + match = name_pattern.match(line) + + if match: + config_name = match.groups()[0] + + f.close() + + path = os.path.split(os.path.split(__file__)[0])[0] # remove ui/space_userpref.py + path = os.path.join(path, "cfg") + + # create config folder if needed + if not os.path.exists(path): + os.mkdir(path) + + path = os.path.join(path, config_name + ".py") + + if self.properties.keep_original: + shutil.copy(self.properties.filepath, path) + else: + shutil.move(self.properties.filepath, path) + + exec("import " + config_name) + + wm = bpy.context.manager + wm.active_keyconfig = wm.keyconfigs[config_name] + + return {'FINISHED'} + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self) + return {'RUNNING_MODAL'} + +# This operator is also used by interaction presets saving - AddPresetBase + + +class WM_OT_keyconfig_export(bpy.types.Operator): + "Export key configuration to a python script" + bl_idname = "wm.keyconfig_export" + bl_label = "Export Key Configuration..." + + filepath = StringProperty(name="File Path", description="Filepath to write file to") + filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'}) + filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'}) + filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'}) + kc_name = StringProperty(name="KeyConfig Name", description="Name to save the key config as") + + def execute(self, context): + if not self.properties.filepath: + raise Exception("Filepath not set") + + f = open(self.properties.filepath, "w") + if not f: + raise Exception("Could not open file") + + wm = context.manager + kc = wm.active_keyconfig + + if self.properties.kc_name != '': + name = self.properties.kc_name + elif kc.name == 'Blender': + name = os.path.splitext(os.path.basename(self.properties.filepath))[0] + else: + name = kc.name + + f.write("# Configuration %s\n" % name) + + f.write("import bpy\n\n") + f.write("wm = bpy.context.manager\n") + f.write("kc = wm.add_keyconfig('%s')\n\n" % name) + + # Generate a list of keymaps to export: + # + # First add all user_defined keymaps (found in inputs.edited_keymaps list), + # then add all remaining keymaps from the currently active custom keyconfig. + # + # This will create a final list of keymaps that can be used as a 'diff' against + # the default blender keyconfig, recreating the current setup from a fresh blender + # without needing to export keymaps which haven't been edited. + + class FakeKeyConfig(): + keymaps = [] + edited_kc = FakeKeyConfig() + edited_kc.keymaps.extend(context.user_preferences.inputs.edited_keymaps) + # merge edited keymaps with non-default keyconfig, if it exists + if kc != wm.default_keyconfig: + export_keymaps = _merge_keymaps(edited_kc, kc) + else: + export_keymaps = _merge_keymaps(edited_kc, edited_kc) + + for km, kc_x in export_keymaps: + + km = km.active() + + f.write("# Map %s\n" % km.name) + f.write("km = kc.add_keymap('%s', space_type='%s', region_type='%s', modal=%s)\n\n" % (km.name, km.space_type, km.region_type, km.modal)) + for kmi in km.items: + if km.modal: + f.write("kmi = km.items.add_modal('%s', '%s', '%s'" % (kmi.propvalue, kmi.type, kmi.value)) + else: + f.write("kmi = km.items.add('%s', '%s', '%s'" % (kmi.idname, kmi.type, kmi.value)) + if kmi.any: + f.write(", any=True") + else: + if kmi.shift: + f.write(", shift=True") + if kmi.ctrl: + f.write(", ctrl=True") + if kmi.alt: + f.write(", alt=True") + if kmi.oskey: + f.write(", oskey=True") + if kmi.key_modifier and kmi.key_modifier != 'NONE': + f.write(", key_modifier='%s'" % kmi.key_modifier) + f.write(")\n") + + def export_properties(prefix, properties): + for pname in dir(properties): + if not properties.is_property_hidden(pname): + value = eval("properties.%s" % pname) + if isinstance(value, bpy.types.OperatorProperties): + export_properties(prefix + "." + pname, value) + elif properties.is_property_set(pname): + value = _string_value(value) + if value != "": + f.write(prefix + ".%s = %s\n" % (pname, value)) + + props = kmi.properties + + if props is not None: + export_properties("kmi.properties", props) + + f.write("\n") + + f.close() + + return {'FINISHED'} + + def invoke(self, context, event): + wm = context.manager + wm.add_fileselect(self) + return {'RUNNING_MODAL'} + + +class WM_OT_keymap_edit(bpy.types.Operator): + "Edit stored key map" + bl_idname = "wm.keymap_edit" + bl_label = "Edit Key Map" + + def execute(self, context): + wm = context.manager + km = context.keymap + km.copy_to_user() + return {'FINISHED'} + + +class WM_OT_keymap_restore(bpy.types.Operator): + "Restore key map(s)" + bl_idname = "wm.keymap_restore" + bl_label = "Restore Key Map(s)" + + all = BoolProperty(attr="all", name="All Keymaps", description="Restore all keymaps to default") + + def execute(self, context): + wm = context.manager + + if self.properties.all: + for km in wm.default_keyconfig.keymaps: + km.restore_to_default() + else: + km = context.keymap + km.restore_to_default() + + return {'FINISHED'} + + +class WM_OT_keyitem_restore(bpy.types.Operator): + "Restore key map item" + bl_idname = "wm.keyitem_restore" + bl_label = "Restore Key Map Item" + + item_id = IntProperty(attr="item_id", name="Item Identifier", description="Identifier of the item to remove") + + def execute(self, context): + wm = context.manager + km = context.keymap + kmi = km.item_from_id(self.properties.item_id) + + km.restore_item_to_default(kmi) + + return {'FINISHED'} + + +class WM_OT_keyitem_add(bpy.types.Operator): + "Add key map item" + bl_idname = "wm.keyitem_add" + bl_label = "Add Key Map Item" + + def execute(self, context): + wm = context.manager + km = context.keymap + kc = wm.default_keyconfig + + if km.modal: + km.items.add_modal("", 'A', 'PRESS') # kmi + else: + km.items.add("none", 'A', 'PRESS') # kmi + + # clear filter and expand keymap so we can see the newly added item + if context.space_data.filter != '': + context.space_data.filter = '' + km.items_expanded = True + km.children_expanded = True + + return {'FINISHED'} + + +class WM_OT_keyitem_remove(bpy.types.Operator): + "Remove key map item" + bl_idname = "wm.keyitem_remove" + bl_label = "Remove Key Map Item" + + item_id = IntProperty(attr="item_id", name="Item Identifier", description="Identifier of the item to remove") + + def execute(self, context): + wm = context.manager + km = context.keymap + kmi = km.item_from_id(self.properties.item_id) + km.remove_item(kmi) + return {'FINISHED'} + + +class WM_OT_keyconfig_remove(bpy.types.Operator): + "Remove key config" + bl_idname = "wm.keyconfig_remove" + bl_label = "Remove Key Config" + + def poll(self, context): + wm = context.manager + return wm.active_keyconfig.user_defined + + def execute(self, context): + wm = context.manager + + keyconfig = wm.active_keyconfig + + module = __import__(keyconfig.name) + + os.remove(module.__file__) + + compiled_path = module.__file__ + "c" # for .pyc + + if os.path.exists(compiled_path): + os.remove(compiled_path) + + wm.remove_keyconfig(keyconfig) + return {'FINISHED'} + + +classes = [ + WM_OT_keyconfig_export, + WM_OT_keyconfig_import, + WM_OT_keyconfig_test, + WM_OT_keyconfig_remove, + WM_OT_keymap_edit, + WM_OT_keymap_restore, + WM_OT_keyitem_add, + WM_OT_keyitem_remove, + WM_OT_keyitem_restore] + + +def register(): + register = bpy.types.register + for cls in classes: + register(cls) + + +def unregister(): + unregister = bpy.types.unregister + for cls in classes: + unregister(cls) + +if __name__ == "__main__": + register() diff --git a/release/scripts/ui/space_view3d.py b/release/scripts/ui/space_view3d.py index 865e132745b..bab3695a0b6 100644 --- a/release/scripts/ui/space_view3d.py +++ b/release/scripts/ui/space_view3d.py @@ -32,13 +32,12 @@ class VIEW3D_HT_header(bpy.types.Header): obj = context.active_object toolsettings = context.tool_settings - row = layout.row() + row = layout.row(align=True) row.template_header() - sub = row.row(align=True) - # Menus if context.area.show_menus: + sub = row.row(align=True) sub.menu("VIEW3D_MT_view") @@ -54,6 +53,7 @@ class VIEW3D_HT_header(bpy.types.Header): else: sub.menu("VIEW3D_MT_object") + row = layout.row() row.template_header_3D() # do in C for now since these buttons cant be both toggle AND exclusive. @@ -81,10 +81,6 @@ class VIEW3D_HT_header(bpy.types.Header): if toolsettings.proportional_editing != 'DISABLED': row.prop(toolsettings, "proportional_editing_falloff", text="", icon_only=True) - # paint save - if mode_string == 'PAINT_TEXTURE': - row.operator("image.save_dirty", text="Save Edited") - # Snap row = layout.row(align=True) row.prop(toolsettings, "snap", text="") @@ -100,8 +96,8 @@ class VIEW3D_HT_header(bpy.types.Header): # OpenGL render row = layout.row(align=True) - row.operator("screen.opengl_render", text="", icon='RENDER_STILL') - props = row.operator("screen.opengl_render", text="", icon='RENDER_ANIMATION') + row.operator("render.opengl", text="", icon='RENDER_STILL') + props = row.operator("render.opengl", text="", icon='RENDER_ANIMATION') props.animation = True # Pose @@ -149,7 +145,7 @@ class VIEW3D_MT_transform(bpy.types.Menu): layout.operator("transform.tosphere", text="To Sphere") layout.operator("transform.shear", text="Shear") layout.operator("transform.warp", text="Warp") - layout.operator("transform.transform", text="Push/Pull").mode = 'PUSHPULL' + layout.operator("transform.push_pull", text="Push/Pull") if context.edit_object and context.edit_object.type == 'ARMATURE': layout.operator("armature.align") else: @@ -211,7 +207,6 @@ class VIEW3D_MT_snap(bpy.types.Menu): layout.operator("view3d.snap_selected_to_grid", text="Selection to Grid") layout.operator("view3d.snap_selected_to_cursor", text="Selection to Cursor") - layout.operator("view3d.snap_selected_to_center", text="Selection to Origin") layout.separator() @@ -254,8 +249,11 @@ class VIEW3D_MT_view(bpy.types.Menu): layout.operator("view3d.viewnumpad", text="Camera").type = 'CAMERA' layout.operator("view3d.viewnumpad", text="Top").type = 'TOP' + layout.operator("view3d.viewnumpad", text="Bottom").type = 'BOTTOM' layout.operator("view3d.viewnumpad", text="Front").type = 'FRONT' + layout.operator("view3d.viewnumpad", text="Back").type = 'BACK' layout.operator("view3d.viewnumpad", text="Right").type = 'RIGHT' + layout.operator("view3d.viewnumpad", text="Left").type = 'LEFT' layout.menu("VIEW3D_MT_view_cameras", text="Cameras") @@ -388,7 +386,7 @@ class VIEW3D_MT_select_object(bpy.types.Menu): layout.operator("object.select_random", text="Random") layout.operator("object.select_mirror", text="Mirror") layout.operator("object.select_by_layer", text="Select All by Layer") - layout.operator_menu_enum("object.select_by_type", "type", "", text="Select All by Type...") + layout.operator_menu_enum("object.select_by_type", "type", text="Select All by Type...") layout.operator("object.select_camera", text="Select Camera") layout.separator() @@ -455,8 +453,8 @@ class VIEW3D_MT_select_particle(bpy.types.Menu): layout.separator() - layout.operator("particle.select_first", text="Roots") - layout.operator("particle.select_last", text="Tips") + layout.operator("particle.select_roots", text="Roots") + layout.operator("particle.select_tips", text="Tips") class VIEW3D_MT_select_edit_mesh(bpy.types.Menu): @@ -475,8 +473,8 @@ class VIEW3D_MT_select_edit_mesh(bpy.types.Menu): layout.separator() - layout.operator("mesh.select_random", text="Random...") - layout.operator("mesh.select_nth", text="Select Nth...") + layout.operator("mesh.select_random", text="Random") + layout.operator("mesh.select_nth", text="Every N Number of Verts") layout.operator("mesh.edges_select_sharp", text="Sharp Edges") layout.operator("mesh.faces_select_linked_flat", text="Linked Flat Faces") layout.operator("mesh.faces_select_interior", text="Interior Faces") @@ -486,8 +484,10 @@ class VIEW3D_MT_select_edit_mesh(bpy.types.Menu): layout.operator("mesh.select_by_number_vertices", text="Triangles").type = 'TRIANGLES' layout.operator("mesh.select_by_number_vertices", text="Quads").type = 'QUADS' + if context.scene.tool_settings.mesh_selection_mode[2] == False: + layout.operator("mesh.select_non_manifold", text="Non Manifold") layout.operator("mesh.select_by_number_vertices", text="Loose Verts/Edges").type = 'OTHER' - layout.operator("mesh.select_similar", text="Similar...") + layout.operator("mesh.select_similar", text="Similar") layout.separator() @@ -523,7 +523,7 @@ class VIEW3D_MT_select_edit_curve(bpy.types.Menu): layout.operator("curve.select_all", text="Select/Deselect All") layout.operator("curve.select_inverse") layout.operator("curve.select_random") - layout.operator("curve.select_every_nth") + layout.operator("curve.select_nth", text="Every Nth Number of Points") layout.separator() @@ -552,7 +552,7 @@ class VIEW3D_MT_select_edit_surface(bpy.types.Menu): layout.operator("curve.select_all", text="Select/Deselect All") layout.operator("curve.select_inverse") layout.operator("curve.select_random") - layout.operator("curve.select_every_nth") + layout.operator("curve.select_nth", text="Every Nth Number of Points") layout.separator() @@ -656,6 +656,7 @@ class VIEW3D_MT_object(bpy.types.Menu): 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.separator() @@ -677,6 +678,11 @@ class VIEW3D_MT_object(bpy.types.Menu): layout.separator() + layout.menu("VIEW3D_MT_object_game_properties") + layout.menu("VIEW3D_MT_object_game_logicbricks") + + layout.separator() + layout.operator("object.join_uvs") layout.operator("object.join") @@ -705,35 +711,86 @@ class VIEW3D_MT_object_specials(bpy.types.Menu): def poll(self, context): # add more special types - obj = context.object - return bool(obj and obj.type == 'LAMP') + return context.object def draw(self, context): layout = self.layout obj = context.object - if obj and obj.type == 'LAMP': + if obj.type == 'CAMERA': layout.operator_context = 'INVOKE_REGION_WIN' - props = layout.operator("wm.context_modal_mouse", text="Spot Size") - props.path_iter = "selected_editable_objects" - props.path_item = "data.spot_size" + props = layout.operator("wm.context_modal_mouse", text="Camera Lens Angle") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.lens" + props.input_scale = 0.1 + + if not obj.data.dof_object: + #layout.label(text="Test Has DOF obj"); + props = layout.operator("wm.context_modal_mouse", text="DOF Distance") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.dof_distance" + props.input_scale = 0.02 + + if obj.type in ('CURVE','TEXT'): + layout.operator_context = 'INVOKE_REGION_WIN' + + props = layout.operator("wm.context_modal_mouse", text="Extrude Size") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.extrude" props.input_scale = 0.01 - props = layout.operator("wm.context_modal_mouse", text="Distance") - props.path_iter = "selected_editable_objects" - props.path_item = "data.distance" - props.input_scale = 0.1 + props = layout.operator("wm.context_modal_mouse", text="Width Size") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.width" + props.input_scale = 0.01 + + if obj.type == 'EMPTY': + layout.operator_context = 'INVOKE_REGION_WIN' + + props = layout.operator("wm.context_modal_mouse", text="Empty Draw Size") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "empty_draw_size" + props.input_scale = 0.01 + + if obj.type == 'LAMP': + layout.operator_context = 'INVOKE_REGION_WIN' + + props = layout.operator("wm.context_modal_mouse", text="Energy") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.energy" + + 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" + props.input_scale = 0.1 + + if obj.data.type == 'SPOT': + layout.separator() + props = layout.operator("wm.context_modal_mouse", text="Spot Size") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.spot_size" + props.input_scale = 0.01 + + props = layout.operator("wm.context_modal_mouse", text="Spot Blend") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.spot_blend" + props.input_scale = -0.01 - props = layout.operator("wm.context_modal_mouse", text="Clip Start") - props.path_iter = "selected_editable_objects" - props.path_item = "data.shadow_buffer_clip_start" - props.input_scale = 0.05 + props = layout.operator("wm.context_modal_mouse", text="Clip Start") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.shadow_buffer_clip_start" + props.input_scale = 0.05 - props = layout.operator("wm.context_modal_mouse", text="Clip End") - props.path_iter = "selected_editable_objects" - props.path_item = "data.shadow_buffer_clip_end" - props.input_scale = 0.05 + props = layout.operator("wm.context_modal_mouse", text="Clip End") + props.data_path_iter = "selected_editable_objects" + props.data_path_item = "data.shadow_buffer_clip_end" + props.input_scale = 0.05 + + layout.separator() + + props = layout.operator("object.isolate_type_render") class VIEW3D_MT_object_apply(bpy.types.Menu): @@ -792,6 +849,7 @@ class VIEW3D_MT_object_constraints(bpy.types.Menu): layout = self.layout layout.operator("object.constraint_add_with_targets") + layout.operator("object.constraints_copy") layout.operator("object.constraints_clear") @@ -834,10 +892,31 @@ class VIEW3D_MT_make_links(bpy.types.Menu): def draw(self, context): layout = self.layout - layout.operator_menu_enum("object.make_links_scene", "type", text="Objects to Scene...") - layout.operator_menu_enum("marker.make_links_scene", "type", text="Markers to Scene...") - layout.operator_enums("object.make_links_data", property="type") # inline + 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 + + +class VIEW3D_MT_object_game_properties(bpy.types.Menu): + bl_label = "Game Properties" + + def draw(self, context): + layout = self.layout + + layout.operator("object.game_property_copy", text="Replace").operation = 'REPLACE' + layout.operator("object.game_property_copy", text="Merge").operation = 'MERGE' + layout.operator_menu_enum("object.game_property_copy", "property", text="Copy...") + layout.separator() + layout.operator("object.game_property_clear") + +class VIEW3D_MT_object_game_logicbricks(bpy.types.Menu): + bl_label = "Logic Bricks" + + def draw(self, context): + layout = self.layout + + layout.operator("object.logic_bricks_copy", text="Copy") # ********** Vertex paint menu ********** @@ -849,6 +928,7 @@ class VIEW3D_MT_paint_vertex(bpy.types.Menu): layout = self.layout layout.operator("paint.vertex_color_set") + layout.operator("paint.vertex_color_dirt") class VIEW3D_MT_hook(bpy.types.Menu): @@ -932,7 +1012,7 @@ class VIEW3D_MT_sculpt(bpy.types.Menu): layout.prop(sculpt, "lock_y") layout.prop(sculpt, "lock_z") layout.separator() - layout.operator_menu_enum("brush.curve_preset", property="shape") + layout.operator_menu_enum("brush.curve_preset", "shape") layout.separator() sculpt_tool = brush.sculpt_tool @@ -991,8 +1071,8 @@ class VIEW3D_MT_particle_specials(bpy.types.Menu): layout.separator() if particle_edit.selection_mode == 'POINT': layout.operator("particle.subdivide") - layout.operator("particle.select_first") - layout.operator("particle.select_last") + layout.operator("particle.select_roots") + layout.operator("particle.select_tips") layout.operator("particle.remove_doubles") @@ -1022,6 +1102,7 @@ class VIEW3D_MT_pose(bpy.types.Menu): 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.separator() @@ -1142,6 +1223,7 @@ class VIEW3D_MT_pose_constraints(bpy.types.Menu): layout = self.layout layout.operator("pose.constraint_add_with_targets", text="Add (With Targets)...") + layout.operator("pose.constraints_copy") layout.operator("pose.constraints_clear") @@ -1243,15 +1325,15 @@ class VIEW3D_MT_edit_mesh_selection_mode(bpy.types.Menu): prop = layout.operator("wm.context_set_value", text="Vertex", icon='VERTEXSEL') prop.value = "(True, False, False)" - prop.path = "tool_settings.mesh_selection_mode" + prop.data_path = "tool_settings.mesh_selection_mode" prop = layout.operator("wm.context_set_value", text="Edge", icon='EDGESEL') prop.value = "(False, True, False)" - prop.path = "tool_settings.mesh_selection_mode" + prop.data_path = "tool_settings.mesh_selection_mode" prop = layout.operator("wm.context_set_value", text="Face", icon='FACESEL') prop.value = "(False, False, True)" - prop.path = "tool_settings.mesh_selection_mode" + prop.data_path = "tool_settings.mesh_selection_mode" class VIEW3D_MT_edit_mesh_extrude(bpy.types.Menu): @@ -1373,7 +1455,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(bpy.types.Operator): totedge = mesh.total_edge_sel totvert = mesh.total_vert_sel - if totface >= 1 or totvert == 1: + if totface >= 1: return bpy.ops.mesh.extrude_region_move('INVOKE_REGION_WIN', TRANSFORM_OT_translate={"constraint_orientation": "NORMAL", "constraint_axis": [False, False, True]}) elif totedge == 1: return bpy.ops.mesh.extrude_region_move('INVOKE_REGION_WIN', TRANSFORM_OT_translate={"constraint_orientation": "NORMAL", "constraint_axis": [True, True, False]}) @@ -1462,12 +1544,14 @@ class VIEW3D_MT_edit_mesh_faces(bpy.types.Menu): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator("mesh.flip_normals") # layout.operator("mesh.bevel") # layout.operator("mesh.bevel") layout.operator("mesh.edge_face_add") layout.operator("mesh.fill") layout.operator("mesh.beautify_fill") layout.operator("mesh.solidify") + layout.operator("mesh.sort_faces") layout.separator() @@ -1623,6 +1707,13 @@ class VIEW3D_MT_edit_text(bpy.types.Menu): layout.menu("VIEW3D_MT_edit_text_chars") + layout.separator() + + layout.operator("font.style_toggle", text="Toggle Bold").style = 'BOLD' + layout.operator("font.style_toggle", text="Toggle Italic").style = 'ITALIC' + layout.operator("font.style_toggle", text="Toggle Underline").style = 'UNDERLINE' + layout.operator("font.style_toggle", text="Toggle Small Caps").style = 'SMALL_CAPS' + class VIEW3D_MT_edit_text_chars(bpy.types.Menu): bl_label = "Special Characters" @@ -1829,7 +1920,7 @@ class VIEW3D_MT_edit_armature_roll(bpy.types.Menu): # ********** Panel ********** -class VIEW3D_PT_3dview_properties(bpy.types.Panel): +class VIEW3D_PT_view3d_properties(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_label = "View" @@ -1845,8 +1936,7 @@ class VIEW3D_PT_3dview_properties(bpy.types.Panel): scene = context.scene col = layout.column() - col.label(text="Camera:") - col.prop(view, "camera", text="") + col.active = view.region_3d.view_perspective != 'CAMERA' col.prop(view, "lens") col.label(text="Lock to Object:") col.prop(view, "lock_object", text="") @@ -1858,10 +1948,15 @@ class VIEW3D_PT_3dview_properties(bpy.types.Panel): col.prop(view, "clip_start", text="Start") col.prop(view, "clip_end", text="End") - layout.column().prop(scene, "cursor_location", text="3D Cursor:") + subcol = col.column() + subcol.enabled = not view.lock_camera_and_layers + subcol.label(text="Local Camera:") + subcol.prop(view, "camera", text="") + + layout.column().prop(view, "cursor_location") -class VIEW3D_PT_3dview_name(bpy.types.Panel): +class VIEW3D_PT_view3d_name(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_label = "Item" @@ -1885,7 +1980,7 @@ class VIEW3D_PT_3dview_name(bpy.types.Panel): row.prop(bone, "name", text="") -class VIEW3D_PT_3dview_display(bpy.types.Panel): +class VIEW3D_PT_view3d_display(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_label = "Display" @@ -1903,9 +1998,11 @@ class VIEW3D_PT_3dview_display(bpy.types.Panel): ob = context.object col = layout.column() - col.prop(view, "display_x_axis", text="X Axis") - col.prop(view, "display_y_axis", text="Y Axis") - col.prop(view, "display_z_axis", text="Z Axis") + col.prop(view, "display_render_override") + + col = layout.column() + display_all = not view.display_render_override + col.active = display_all col.prop(view, "outline_selected") col.prop(view, "all_object_origins") col.prop(view, "relationship_lines") @@ -1914,9 +2011,17 @@ class VIEW3D_PT_3dview_display(bpy.types.Panel): col.prop(mesh, "all_edges") col = layout.column() - col.prop(view, "display_floor", text="Grid Floor") + col.active = display_all + split = col.split(percentage=0.55) + split.prop(view, "display_floor", text="Grid Floor") + + row = split.row(align=True) + row.prop(view, "display_x_axis", text="X", toggle=True) + row.prop(view, "display_y_axis", text="Y", toggle=True) + row.prop(view, "display_z_axis", text="Z", toggle=True) + sub = col.column(align=True) - sub.active = view.display_floor + sub.active = (display_all and view.display_floor) sub.prop(view, "grid_lines", text="Lines") sub.prop(view, "grid_spacing", text="Spacing") sub.prop(view, "grid_subdivisions", text="Subdivisions") @@ -1943,7 +2048,7 @@ class VIEW3D_PT_3dview_display(bpy.types.Panel): row.prop(region, "box_clip") -class VIEW3D_PT_3dview_meshdisplay(bpy.types.Panel): +class VIEW3D_PT_view3d_meshdisplay(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_label = "Mesh Display" @@ -1979,7 +2084,7 @@ class VIEW3D_PT_3dview_meshdisplay(bpy.types.Panel): col.prop(mesh, "draw_face_area") -class VIEW3D_PT_3dview_curvedisplay(bpy.types.Panel): +class VIEW3D_PT_view3d_curvedisplay(bpy.types.Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_label = "Curve Display" @@ -2029,7 +2134,7 @@ class VIEW3D_PT_background_image(bpy.types.Panel): layout.active = view.display_background_images box = layout.box() row = box.row(align=True) - row.prop(bg, "show_expanded", text="", no_bg=True) + row.prop(bg, "show_expanded", text="", emboss=False) row.label(text=getattr(bg.image, "name", "Not Set")) row.operator("view3d.remove_background_image", text="", icon='X').index = i @@ -2115,6 +2220,7 @@ class VIEW3D_PT_etch_a_ton(bpy.types.Panel): col.prop(toolsettings, "etch_autoname") col.prop(toolsettings, "etch_number") col.prop(toolsettings, "etch_side") + col.operator("sketch.convert", text="Convert") class VIEW3D_PT_context_properties(bpy.types.Panel): @@ -2192,6 +2298,8 @@ classes = [ VIEW3D_MT_object_showhide, VIEW3D_MT_make_single_user, VIEW3D_MT_make_links, + VIEW3D_MT_object_game_properties, + VIEW3D_MT_object_game_logicbricks, VIEW3D_MT_hook, VIEW3D_MT_vertex_group, @@ -2247,11 +2355,11 @@ classes = [ VIEW3D_MT_armature_specials, # Only as a menu for keybindings # Panels - VIEW3D_PT_3dview_properties, - VIEW3D_PT_3dview_display, - VIEW3D_PT_3dview_name, - VIEW3D_PT_3dview_meshdisplay, - VIEW3D_PT_3dview_curvedisplay, + VIEW3D_PT_view3d_properties, + VIEW3D_PT_view3d_display, + VIEW3D_PT_view3d_name, + VIEW3D_PT_view3d_meshdisplay, + VIEW3D_PT_view3d_curvedisplay, VIEW3D_PT_background_image, VIEW3D_PT_transform_orientations, VIEW3D_PT_etch_a_ton, diff --git a/release/scripts/ui/space_view3d_toolbar.py b/release/scripts/ui/space_view3d_toolbar.py index 6de4ba6ce34..515fa236fc0 100644 --- a/release/scripts/ui/space_view3d_toolbar.py +++ b/release/scripts/ui/space_view3d_toolbar.py @@ -19,6 +19,7 @@ # <pep8 compliant> import bpy +narrowui = bpy.context.user_preferences.view.properties_width_check class View3DPanel(bpy.types.Panel): bl_space_type = 'VIEW_3D' @@ -42,6 +43,9 @@ class VIEW3D_PT_tools_objectmode(View3DPanel): col.operator("transform.resize", text="Scale") col = layout.column(align=True) + col.operator("object.origin_set", text="Origin") + + col = layout.column(align=True) col.label(text="Object:") col.operator("object.duplicate_move") col.operator("object.delete") @@ -500,18 +504,26 @@ class VIEW3D_PT_tools_brush(PaintPanel): if not context.particle_edit_object: col = layout.split().column() - row = col.row() - if context.sculpt_object and brush: - defaultbrushes = 8 - elif context.texture_paint_object and brush: - defaultbrushes = 4 + if context.sculpt_object and context.tool_settings.sculpt: + col.template_ID_preview(settings, "brush", new="brush.add", filter="is_sculpt_brush", rows=3, cols=8) + elif context.texture_paint_object and context.tool_settings.image_paint: + col.template_ID_preview(settings, "brush", new="brush.add", filter="is_imapaint_brush", rows=3, cols=8) + elif context.vertex_paint_object and context.tool_settings.vertex_paint: + col.template_ID_preview(settings, "brush", new="brush.add", filter="is_vpaint_brush", rows=3, cols=8) + elif context.weight_paint_object and context.tool_settings.weight_paint: + col.template_ID_preview(settings, "brush", new="brush.add", filter="is_wpaint_brush", rows=3, cols=8) else: - defaultbrushes = 7 + row = col.row() - row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaultbrushes) + if context.sculpt_object and brush: + defaultbrushes = 8 + elif context.texture_paint_object and brush: + defaultbrushes = 4 + else: + defaultbrushes = 7 - col.template_ID(settings, "brush", new="brush.add") + row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaultbrushes) # Particle Mode # @@ -523,9 +535,11 @@ class VIEW3D_PT_tools_brush(PaintPanel): if settings.tool != 'NONE': col = layout.column() col.prop(brush, "size", slider=True) - col.prop(brush, "strength", slider=True) + if settings.tool != 'ADD': + col.prop(brush, "strength", slider=True) if settings.tool == 'ADD': + col.prop(brush, "count") col = layout.column() col.prop(settings, "add_interpolate") sub = col.column(align=True) @@ -541,33 +555,150 @@ class VIEW3D_PT_tools_brush(PaintPanel): # Sculpt Mode # elif context.sculpt_object and brush: + edit = context.user_preferences.edit + col = layout.column() + + col.separator() row = col.row(align=True) - row.prop(brush, "size", slider=True) - if brush.sculpt_tool != 'GRAB': - row.prop(brush, "use_size_pressure", toggle=True, text="") + if edit.sculpt_paint_use_unified_size: + if edit.sculpt_paint_unified_lock_brush_size: + row.prop(edit, "sculpt_paint_unified_lock_brush_size", toggle=True, text="", icon='LOCKED') + row.prop(edit, "sculpt_paint_unified_unprojected_radius", text="Unified Radius", slider=True) + else: + row.prop(edit, "sculpt_paint_unified_lock_brush_size", toggle=True, text="", icon='UNLOCKED') + row.prop(edit, "sculpt_paint_unified_size", text="Unified Radius", slider=True) + + else: + if brush.lock_brush_size: + row.prop(brush, "lock_brush_size", toggle=True, text="", icon='LOCKED') + row.prop(brush, "unprojected_radius", text="Radius", slider=True) + else: + row.prop(brush, "lock_brush_size", toggle=True, text="", icon='UNLOCKED') + row.prop(brush, "size", text="Radius", slider=True) + + row.prop(brush, "use_size_pressure", toggle=True, text="") + + + if brush.sculpt_tool not in ('SNAKE_HOOK', 'GRAB', 'ROTATE'): + col.separator() row = col.row(align=True) - row.prop(brush, "strength", slider=True) + + 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: + row.prop(brush, "use_space_atten", toggle=True, text="", icon='UNLOCKED') + + if edit.sculpt_paint_use_unified_strength: + row.prop(edit, "sculpt_paint_unified_strength", text="Unified Strength", slider=True) + else: + row.prop(brush, "strength", text="Strength", slider=True) + row.prop(brush, "use_strength_pressure", text="") - # XXX - TODO - #row = col.row(align=True) - #row.prop(brush, "jitter", slider=True) - #row.prop(brush, "use_jitter_pressure", toggle=True, text="") - col = layout.column() - if brush.sculpt_tool in ('DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'): - col.row().prop(brush, "direction", expand=True) + if brush.sculpt_tool not in ('SMOOTH'): + col.separator() + + row = col.row(align=True) + row.prop(brush, "autosmooth_factor", slider=True) + row.prop(brush, "use_inverse_smooth_pressure", toggle=True, text="") + + + + 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'): + 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'): + row = col.row(align=True) + + col.separator() + + if brush.use_original_normal: + row.prop(brush, "use_original_normal", toggle=True, text="", icon='LOCKED') + else: + row.prop(brush, "use_original_normal", toggle=True, text="", icon='UNLOCKED') + + 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'): + row = col.row(align=True) + row.prop(brush, "plane_offset", slider=True) + row.prop(brush, "use_offset_pressure", text="") + + col.separator() + + row= col.row() + row.prop(brush, "use_plane_trim", text="Trim") + row= col.row() + row.active=brush.use_plane_trim + row.prop(brush, "plane_trim", slider=True, text="Distance") + + col.separator() + + row= col.row() + row.prop(brush, "use_frontface", text="Front Faces Only") + + #if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'LAYER', 'CLAY', 'CLAY_TUBES'): + if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'LAYER', 'CLAY'): + col.separator() + col.row().prop(brush, "direction", expand=True) + elif brush.sculpt_tool in ('FLATTEN'): + col.separator() + col.row().prop(brush, "flatten_contrast", expand=True) + elif brush.sculpt_tool in ('FILL'): + col.separator() + col.row().prop(brush, "fill_deepen", expand=True) + elif brush.sculpt_tool in ('SCRAPE'): + col.separator() + col.row().prop(brush, "scrape_peaks", expand=True) + elif brush.sculpt_tool in ('INFLATE'): + col.separator() + col.row().prop(brush, "inflate_deflate", expand=True) + elif brush.sculpt_tool in ('PINCH'): + col.separator() + col.row().prop(brush, "pinch_magnify", expand=True) + + + + #if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'INFLATE', 'LAYER', 'CLAY', 'CLAY_TUBES'): + if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'INFLATE', 'LAYER', 'CLAY'): + col.separator() + + col.prop(brush, "use_accumulate") + - if brush.sculpt_tool in ('DRAW', 'INFLATE', 'LAYER'): - col.prop(brush, "use_accumulate") - if brush.sculpt_tool == 'LAYER': + if brush.sculpt_tool == 'LAYER': + col.separator() + + ob = context.sculpt_object + do_persistent = True + + # not supported yet for this case + for md in ob.modifiers: + if md.type == 'MULTIRES': + do_persistent = False + + if do_persistent: col.prop(brush, "use_persistent") col.operator("sculpt.set_persistent_base") @@ -592,6 +723,11 @@ class VIEW3D_PT_tools_brush(PaintPanel): col.prop(brush, "blend", text="Blend") + col = layout.column() + col.active = (brush.blend not in ('ERASE_ALPHA', 'ADD_ALPHA')) + col.prop(brush, "use_alpha") + + # Weight Paint Mode # elif context.weight_paint_object and brush: @@ -650,9 +786,88 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel): col = layout.column() - col.template_ID_preview(brush, "texture", new="texture.new", rows=2, cols=4) + col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8) + + if context.sculpt_object: + #XXX duplicated from properties_texture.py + + wide_ui = context.region.width > narrowui + + + col.separator() + + + col.label(text="Brush Mapping:") + row = col.row(align=True) + row.prop(tex_slot, "map_mode", expand=True) + + col.separator() + + col = layout.column() + 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'): + col.prop(brush, "texture_angle_source", 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 = col.row(align=True) + + #col = row.column() + #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 = 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') + + split = layout.split() + + col = split.column() + col.prop(tex_slot, "offset") + + if wide_ui: + col = split.column() + else: + col.separator() + + col.prop(tex_slot, "size") + + col = layout.column() + + row = col.row(align=True) + row.label(text="Sample Bias:") + row = col.row(align=True) + row.prop(brush, "texture_sample_bias", slider=True, text="") + + row = col.row(align=True) + row.label(text="Overlay:") + row.active = tex_slot.map_mode in ('FIXED', 'TILED') + + row = col.row(align=True) + + col = row.column() + + if brush.use_texture_overlay: + col.prop(brush, "use_texture_overlay", toggle=True, text="", icon='MUTE_IPO_OFF') + else: + col.prop(brush, "use_texture_overlay", toggle=True, text="", icon='MUTE_IPO_ON') + + col.active = tex_slot.map_mode in ('FIXED', 'TILED') - col.row().prop(tex_slot, "map_mode", expand=True) + 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 class VIEW3D_PT_tools_brush_tool(PaintPanel): @@ -676,15 +891,11 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel): col = layout.column(align=True) if context.sculpt_object: - col.prop(brush, "sculpt_tool", expand=True) + col.prop(brush, "sculpt_tool", expand=False, text="") elif context.texture_paint_object: - col.prop(brush, "imagepaint_tool", expand=True) - #col.prop_enum(settings, "tool", 'DRAW') - #col.prop_enum(settings, "tool", 'SOFTEN') - #col.prop_enum(settings, "tool", 'CLONE') - #col.prop_enum(settings, "tool", 'SMEAR') + col.prop(brush, "imagepaint_tool", expand=False, text="") elif context.vertex_paint_object or context.weight_paint_object: - col.prop(brush, "vertexpaint_tool", expand=True) + col.prop(brush, "vertexpaint_tool", expand=False, text="") class VIEW3D_PT_tools_brush_stroke(PaintPanel): @@ -705,29 +916,83 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel): brush = settings.brush texture_paint = context.texture_paint_object + col = layout.column() + if context.sculpt_object: - if brush.sculpt_tool != 'LAYER': - layout.prop(brush, "use_anchor") - layout.prop(brush, "use_rake") + col.label(text="Stroke Method:") + col.prop(brush, "stroke_method", text="") + + if brush.use_anchor: + col.separator() + row = col.row() + row.prop(brush, "edge_to_edge", "Edge To Edge") + + if brush.use_airbrush: + col.separator() + row = col.row() + row.prop(brush, "rate", text="Rate", slider=True) + + if brush.use_space: + col.separator() + row = col.row() + 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.restore_mesh): + col = layout.column() + col.separator() - layout.prop(brush, "use_airbrush") - col = layout.column() - col.active = brush.use_airbrush - col.prop(brush, "rate", slider=True) + col.prop(brush, "use_smooth_stroke") + + sub = col.column() + sub.active = brush.use_smooth_stroke + sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + + col.separator() + + row = col.row(align=True) + row.prop(brush, "jitter", slider=True) + row.prop(brush, "use_jitter_pressure", toggle=True, text="") + + else: + row = col.row() + row.prop(brush, "use_airbrush") + + row = col.row() + row.active = brush.use_airbrush and (not brush.use_space) and (not brush.use_anchor) + row.prop(brush, "rate", slider=True) + + col.separator() + + if not texture_paint: + row = col.row() + row.prop(brush, "use_smooth_stroke") + + col = layout.column() + col.active = brush.use_smooth_stroke + col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) + col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + + col.separator() - if not texture_paint: - layout.prop(brush, "use_smooth_stroke") col = layout.column() - col.active = brush.use_smooth_stroke - col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True) - col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True) + col.active = (not brush.use_anchor) and (brush.sculpt_tool not in ('GRAB', 'THUMB', 'ROTATE', 'SNAKE_HOOK')) - layout.prop(brush, "use_space") - row = layout.row(align=True) - row.active = brush.use_space - row.prop(brush, "spacing", text="Distance", slider=True) - if texture_paint: - row.prop(brush, "use_spacing_pressure", toggle=True, text="") + row = col.row() + row.prop(brush, "use_space") + + row = col.row() + row.active = brush.use_space + row.prop(brush, "spacing", text="Spacing") + + #col.prop(brush, "use_space_atten", text="Adaptive Strength") + #col.prop(brush, "use_adaptive_space", text="Adaptive Spacing") + + #col.separator() + + #if texture_paint: + # row.prop(brush, "use_spacing_pressure", toggle=True, text="") class VIEW3D_PT_tools_brush_curve(PaintPanel): @@ -745,11 +1010,19 @@ class VIEW3D_PT_tools_brush_curve(PaintPanel): brush = settings.brush layout.template_curve_mapping(brush, "curve", brush=True) - layout.operator_menu_enum("brush.curve_preset", property="shape") + row = layout.row(align=True) + row.operator("brush.curve_preset", icon="SMOOTHCURVE", text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon="SPHERECURVE", text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon="ROOTCURVE", text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon="SHARPCURVE", text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon="LINCURVE", text="").shape = 'LINE' + row.operator("brush.curve_preset", icon="NOCURVE", text="").shape = 'MAX' + row.operator("brush.curve_preset", icon="RNDCURVE", text="").shape = 'MID9' class VIEW3D_PT_sculpt_options(PaintPanel): bl_label = "Options" + bl_default_closed = True def poll(self, context): return (context.sculpt_object and context.tool_settings.sculpt) @@ -757,25 +1030,100 @@ class VIEW3D_PT_sculpt_options(PaintPanel): def draw(self, context): layout = self.layout + wide_ui = context.region.width > narrowui + sculpt = context.tool_settings.sculpt + settings = self.paint_settings(context) + brush = settings.brush - col = layout.column() - col.prop(sculpt, "show_brush") - col.prop(sculpt, "fast_navigate") + split = layout.split() + + col = split.column() - split = self.layout.split() + edit = context.user_preferences.edit + col.label(text="Unified Settings:") + col.prop(edit, "sculpt_paint_use_unified_size", text="Size") + col.prop(edit, "sculpt_paint_use_unified_strength", text="Strength") + + if wide_ui: + col = split.column() + else: + col.separator() + + col.label(text="Lock:") + row = col.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) + +class VIEW3D_PT_sculpt_symmetry(PaintPanel): + bl_label = "Symmetry" + bl_default_closed = True + + def poll(self, context): + return (context.sculpt_object and context.tool_settings.sculpt) + + def draw(self, context): + wide_ui = context.region.width > narrowui + + layout = self.layout + + sculpt = context.tool_settings.sculpt + settings = self.paint_settings(context) + brush = settings.brush + + split = layout.split() col = split.column() - col.label(text="Symmetry:") + + col.label(text="Mirror:") col.prop(sculpt, "symmetry_x", text="X") col.prop(sculpt, "symmetry_y", text="Y") col.prop(sculpt, "symmetry_z", text="Z") - col = split.column() - col.label(text="Lock:") - col.prop(sculpt, "lock_x", text="X") - col.prop(sculpt, "lock_y", text="Y") - col.prop(sculpt, "lock_z", text="Z") + if wide_ui: + col = split.column() + else: + col.separator() + + col.prop(sculpt, "radial_symm", text="Radial") + + col = layout.column() + + col.separator() + + col.prop(sculpt, "use_symmetry_feather", text="Feather") + +class VIEW3D_PT_tools_brush_appearance(PaintPanel): + bl_label = "Appearance" + bl_default_closed = True + + def poll(self, 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) + + def draw(self, context): + layout = self.layout + + sculpt = context.tool_settings.sculpt + settings = self.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', 'CLAY_TUBES', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN'): + if brush.sculpt_tool in ('DRAW', 'INFLATE', 'CLAY', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN'): + col.prop(brush, "add_col", text="Add Color") + col.prop(brush, "sub_col", text="Substract Color") + else: + col.prop(brush, "add_col", text="Color") + + col.separator() + + col = layout.column() + col.label(text="Icon:") + #col.template_ID_preview(brush, "image_icon", open="image.open", filter="is_image_icon", rows=3, cols=8) + col.template_ID_preview(brush, "image_icon", open="image.open", rows=3, cols=8) # ********** default tools for weightpaint **************** @@ -907,6 +1255,18 @@ class VIEW3D_PT_tools_projectpaint(View3DPanel): sub = col.column() sub.prop(ipaint, "seam_bleed") + col.label(text="External Editing") + row = col.split(align=True, percentage=0.55) + row.operator("image.project_edit", text="Quick Edit") + row.operator("image.project_apply", text="Apply") + row = col.row(align=True) + row.prop(ipaint, "screen_grab_size", text="") + + sub = col.column() + sub.operator("paint.project_image", text="Apply Camera Image") + + sub.operator("image.save_dirty", text="Save All Edited") + class VIEW3D_MT_tools_projectpaint_clone(bpy.types.Menu): bl_label = "Clone Layer" @@ -915,7 +1275,7 @@ class VIEW3D_MT_tools_projectpaint_clone(bpy.types.Menu): layout = self.layout for i, tex in enumerate(context.active_object.data.uv_textures): prop = layout.operator("wm.context_set_int", text=tex.name) - prop.path = "active_object.data.uv_texture_clone_index" + prop.data_path = "active_object.data.uv_texture_clone_index" prop.value = i @@ -926,7 +1286,7 @@ class VIEW3D_MT_tools_projectpaint_stencil(bpy.types.Menu): layout = self.layout for i, tex in enumerate(context.active_object.data.uv_textures): prop = layout.operator("wm.context_set_int", text=tex.name) - prop.path = "active_object.data.uv_texture_stencil_index" + prop.data_path = "active_object.data.uv_texture_stencil_index" prop.value = i @@ -986,12 +1346,15 @@ class VIEW3D_PT_tools_particlemode(View3DPanel): col.active = pe.editable col.label(text="Draw:") col.prop(pe, "draw_step", text="Path Steps") - if pe.type == 'PARTICLES': - col.prop(pe, "draw_particles", text="Particles") - col.prop(pe, "fade_time") - sub = col.row() - sub.active = pe.fade_time - sub.prop(pe, "fade_frames", slider=True) + if pe.hair: + col.prop(pe, "draw_particles", text="Children") + else: + if pe.type == 'PARTICLES': + col.prop(pe, "draw_particles", text="Particles") + col.prop(pe, "fade_time") + sub = col.row() + sub.active = pe.fade_time + sub.prop(pe, "fade_frames", slider=True) classes = [ @@ -1010,9 +1373,11 @@ classes = [ VIEW3D_PT_tools_posemode_options, VIEW3D_PT_tools_brush, VIEW3D_PT_tools_brush_texture, - VIEW3D_PT_tools_brush_tool, VIEW3D_PT_tools_brush_stroke, VIEW3D_PT_tools_brush_curve, + VIEW3D_PT_tools_brush_appearance, + VIEW3D_PT_tools_brush_tool, + VIEW3D_PT_sculpt_symmetry, VIEW3D_PT_sculpt_options, VIEW3D_PT_tools_vertexpaint, VIEW3D_PT_tools_weightpaint_options, |