diff options
author | Campbell Barton <ideasman42@gmail.com> | 2010-09-01 16:11:34 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2010-09-01 16:11:34 +0400 |
commit | a89c526a92a9f3568d4c0dad139d608518edecbb (patch) | |
tree | 1c803ec9baeb56d0e90efee17aadd9c503318656 /release | |
parent | 7532bc23254b6efbee26f69b02da04d66b32c0bb (diff) |
finished moving importers and exporters into python packages (as proposed on the mailing list).
- made operator dir's into python packages
- lazy loading of module which do the actual import and export (faster blender load times)
- general maintanance and small fixes.
- bugfix for exporting x3d materials
- leak fix for exporting 3ds
Diffstat (limited to 'release')
19 files changed, 1050 insertions, 1247 deletions
diff --git a/release/scripts/io/netrender/__init__.py b/release/scripts/io/netrender/__init__.py index 5a705e95aa8..e4f6bf65fe2 100644 --- a/release/scripts/io/netrender/__init__.py +++ b/release/scripts/io/netrender/__init__.py @@ -19,9 +19,7 @@ # This directory is a Python package. # To support reload properly, try to access a package var, if it's there, reload everything -try: - init_data - +if "bpy" in locals(): reload(model) reload(operators) reload(client) @@ -32,7 +30,7 @@ try: reload(balancing) reload(ui) reload(repath) -except: +else: from netrender import model from netrender import operators from netrender import client @@ -49,7 +47,6 @@ slaves = [] blacklist = [] init_file = "" -init_data = True init_address = True def register(): diff --git a/release/scripts/modules/io_utils.py b/release/scripts/modules/io_utils.py index b65ada8d804..db783dd1054 100644 --- a/release/scripts/modules/io_utils.py +++ b/release/scripts/modules/io_utils.py @@ -42,6 +42,23 @@ class ImportHelper: return {'RUNNING_MODAL'} +# limited replacement for BPyImage.comprehensiveImageLoad +def load_image(imagepath, dirname): + + if os.path.exists(imagepath): + return bpy.data.images.load(imagepath) + + variants = [imagepath, os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] + + for filepath in variants: + for nfilepath in (filepath, bpy.path.resolve_ncase(filepath)): + if os.path.exists(nfilepath): + return bpy.data.images.load(nfilepath) + + # TODO comprehensiveImageLoad also searched in bpy.config.textureDir + return None + + # return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects() def create_derived_objects(scene, ob): if ob.parent and ob.parent.dupli_type != 'NONE': @@ -54,42 +71,16 @@ def create_derived_objects(scene, ob): return False, [(ob, ob.matrix_world)] - def free_derived_objects(ob): ob.free_dupli_list() -# So 3ds max can open files, limit names to 12 in length -# this is verry annoying for filenames! -name_unique = [] -name_mapping = {} -def sane_name(name): - name_fixed = name_mapping.get(name) - if name_fixed != None: - return name_fixed - - if len(name) > 12: - new_name = name[:12] - else: - new_name = name - - i = 0 - - while new_name in name_unique: - new_name = new_name[:-4] + '.%.3d' % i - i+=1 - - name_unique.append(new_name) - name_mapping[name] = new_name - return new_name - - def unpack_list(list_of_tuples): flat_list = [] flat_list_extend = flat_list.extend # a tich faster for t in list_of_tuples: flat_list_extend(t) - return l + return flat_list # same as above except that it adds 0 for triangle faces def unpack_face_list(list_of_tuples): diff --git a/release/scripts/op/io_anim_bvh/__init__.py b/release/scripts/op/io_anim_bvh/__init__.py new file mode 100644 index 00000000000..6b529f87dd7 --- /dev/null +++ b/release/scripts/op/io_anim_bvh/__init__.py @@ -0,0 +1,74 @@ +# ##### 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> + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + # only reload if we alredy loaded, highly annoying + import sys + reload(sys.modules.get("io_mesh_ply.export_ply", sys)) + + +import bpy +from bpy.props import * +from io_utils import ImportHelper + + +class BvhImporter(bpy.types.Operator, ImportHelper): + '''Load a OBJ Motion Capture File''' + bl_idname = "import_anim.bvh" + bl_label = "Import BVH" + + filename_ext = ".bvh" + + 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) + 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"), + ('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"), + ), + name="Rotation", + description="Rotation conversion.", + default='NATIVE') + + def execute(self, context): + import io_anim_bvh.import_bvh + return io_anim_bvh.import_bvh.load(self, context, **self.properties) + + +def menu_func(self, context): + self.layout.operator(BvhImporter.bl_idname, text="Motion Capture (.bvh)") + + +def register(): + bpy.types.INFO_MT_file_import.append(menu_func) + + +def unregister(): + bpy.types.INFO_MT_file_import.remove(menu_func) + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/io_anim_bvh/import_bvh.py b/release/scripts/op/io_anim_bvh/import_bvh.py index 21b3e5e9aa1..befd6cdbe89 100644 --- a/release/scripts/op/io_anim_bvh/import_bvh.py +++ b/release/scripts/op/io_anim_bvh/import_bvh.py @@ -555,67 +555,24 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM return arm_ob -from bpy.props import * -from io_utils import ImportHelper +def load(operator, context, filepath="", rotate_mode='NATIVE', scale=1.0, use_cyclic=False, frame_start=1): + import time + t1 = time.time() + print('\tparsing bvh %r...' % filepath, end="") + bvh_nodes = read_bvh(context, filepath, + ROT_MODE=rotate_mode, + GLOBAL_SCALE=scale) -class BvhImporter(bpy.types.Operator, ImportHelper): - '''Load a OBJ Motion Capture File''' - bl_idname = "import_anim.bvh" - bl_label = "Import BVH" - - filename_ext = ".bvh" - - 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) - 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"), - ('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"), - ), - name="Rotation", - description="Rotation conversion.", - default='NATIVE') - - def execute(self, context): - # print("Selected: " + context.active_object.name) - import time - t1 = time.time() - print('\tparsing bvh...', end="") - - bvh_nodes = read_bvh(context, self.properties.filepath, - ROT_MODE=self.properties.rotate_mode, - GLOBAL_SCALE=self.properties.scale) - - print('%.4f' % (time.time() - t1)) - t1 = time.time() - print('\timporting to blender...', end="") - - bvh_node_dict2armature(context, bvh_nodes, - ROT_MODE=self.properties.rotate_mode, - IMPORT_START_FRAME=self.properties.frame_start, - IMPORT_LOOP=self.properties.loop) - - print('Done in %.4f\n' % (time.time() - t1)) - return {'FINISHED'} + print('%.4f' % (time.time() - t1)) + t1 = time.time() + print('\timporting to blender...', end="") + bvh_node_dict2armature(context, bvh_nodes, + ROT_MODE=rotate_mode, + IMPORT_START_FRAME=frame_start, + IMPORT_LOOP=use_cyclic) -def menu_func(self, context): - self.layout.operator(BvhImporter.bl_idname, text="Motion Capture (.bvh)") - - -def register(): - bpy.types.INFO_MT_file_import.append(menu_func) - - -def unregister(): - bpy.types.INFO_MT_file_import.remove(menu_func) - -if __name__ == "__main__": - register() + print('Done in %.4f\n' % (time.time() - t1)) + + return {'FINISHED'} diff --git a/release/scripts/op/io_mesh_ply/__init__.py b/release/scripts/op/io_mesh_ply/__init__.py new file mode 100644 index 00000000000..c174b16f46c --- /dev/null +++ b/release/scripts/op/io_mesh_ply/__init__.py @@ -0,0 +1,76 @@ +# ##### 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 ##### + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + import sys + reload(sys.modules.get("io_mesh_ply.export_ply", sys)) + + +import bpy +from bpy.props import * +from io_utils import ExportHelper + + +class ExportPLY(bpy.types.Operator, ExportHelper): + '''Export a single object as a stanford PLY with normals, colours and texture coordinates.''' + bl_idname = "export.ply" + bl_label = "Export PLY" + + filename_ext = ".ply" + + 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) + use_uv_coords = BoolProperty(name="UVs", description="Exort the active UV layer", default=True) + use_colors = BoolProperty(name="Vertex Colors", description="Exort the active vertex color layer", default=True) + + @classmethod + def poll(cls, context): + return context.active_object != None + + def execute(self, context): + filepath = self.properties.filepath + filepath = bpy.path.ensure_ext(filepath, self.filename_ext) + import io_mesh_ply.export_ply + return io_mesh_ply.export_ply.save(self, context, **self.properties) + + def draw(self, context): + layout = self.layout + props = self.properties + + row = layout.row() + row.prop(props, "use_modifiers") + row.prop(props, "use_normals") + row = layout.row() + row.prop(props, "use_uv_coords") + row.prop(props, "use_colors") + + +def menu_func(self, context): + self.layout.operator(ExportPLY.bl_idname, text="Stanford (.ply)") + + +def register(): + bpy.types.INFO_MT_file_export.append(menu_func) + + +def unregister(): + bpy.types.INFO_MT_file_export.remove(menu_func) + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/io_mesh_ply/export_ply.py b/release/scripts/op/io_mesh_ply/export_ply.py index f6b33239fe7..aef4df43841 100644 --- a/release/scripts/op/io_mesh_ply/export_ply.py +++ b/release/scripts/op/io_mesh_ply/export_ply.py @@ -21,79 +21,64 @@ # Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za # Contributors: Bruce Merry, Campbell Barton -import bpy - """ This script exports Stanford PLY files from Blender. It supports normals, colours, and texture coordinates per face or per vertex. Only one mesh can be exported at a time. """ -def rvec3d(v): - return round(v[0], 6), round(v[1], 6), round(v[2], 6) - +import bpy +import os -def rvec2d(v): - return round(v[0], 6), round(v[1], 6) +def save(operator, context, filepath="", use_modifiers=True, use_normals=True, use_uv_coords=True, use_colors=True): + + def rvec3d(v): + return round(v[0], 6), round(v[1], 6), round(v[2], 6) -def write(filename, scene, ob, \ - EXPORT_APPLY_MODIFIERS=True,\ - EXPORT_NORMALS=True,\ - EXPORT_UV=True,\ - EXPORT_COLORS=True): - if not filename.lower().endswith('.ply'): - filename += '.ply' + def rvec2d(v): + return round(v[0], 6), round(v[1], 6) + + scene = context.scene + obj = context.object - if not ob: + if not obj: raise Exception("Error, Select 1 active object") - return - file = open(filename, 'w') + file = open(filepath, 'w') - - #EXPORT_EDGES = Draw.Create(0) - """ - is_editmode = Blender.Window.EditMode() - if is_editmode: - Blender.Window.EditMode(0, '', 0) - - Window.WaitCursor(1) - """ if scene.objects.active: 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(scene, True, 'PREVIEW') + if use_modifiers: + mesh = obj.create_mesh(scene, True, 'PREVIEW') else: - mesh = ob.data + mesh = obj.data if not mesh: - raise ("Error, could not get mesh data from active object") - return + raise Exception("Error, could not get mesh data from active object") - # mesh.transform(ob.matrix_world) # XXX + # mesh.transform(obj.matrix_world) # XXX faceUV = (len(mesh.uv_textures) > 0) vertexUV = (len(mesh.sticky) > 0) vertexColors = len(mesh.vertex_colors) > 0 if (not faceUV) and (not vertexUV): - EXPORT_UV = False + use_uv_coords = False if not vertexColors: - EXPORT_COLORS = False + use_colors = False - if not EXPORT_UV: + if not use_uv_coords: faceUV = vertexUV = False - if not EXPORT_COLORS: + if not use_colors: vertexColors = False if faceUV: active_uv_layer = mesh.uv_textures.active if not active_uv_layer: - EXPORT_UV = False + use_uv_coords = False faceUV = None else: active_uv_layer = active_uv_layer.data @@ -101,7 +86,7 @@ def write(filename, scene, ob, \ if vertexColors: active_col_layer = mesh.vertex_colors.active if not active_col_layer: - EXPORT_COLORS = False + use_colors = False vertexColors = None else: active_col_layer = active_col_layer.data @@ -166,7 +151,7 @@ def write(filename, scene, ob, \ file.write('ply\n') file.write('format ascii 1.0\n') - 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('comment Created by Blender %s - www.blender.org, source file: %r\n' % (bpy.app.version_string, os.path.basename(bpy.data.filepath))) file.write('element vertex %d\n' % len(ply_verts)) @@ -174,14 +159,14 @@ def write(filename, scene, ob, \ file.write('property float y\n') file.write('property float z\n') - if EXPORT_NORMALS: + if use_normals: file.write('property float nx\n') file.write('property float ny\n') file.write('property float nz\n') - if EXPORT_UV: + if use_uv_coords: file.write('property float s\n') file.write('property float t\n') - if EXPORT_COLORS: + if use_colors: file.write('property uchar red\n') file.write('property uchar green\n') file.write('property uchar blue\n') @@ -192,11 +177,11 @@ 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: + if use_normals: file.write('%.6f %.6f %.6f ' % v[1]) # no - if EXPORT_UV: + if use_uv_coords: file.write('%.6f %.6f ' % v[2]) # uv - if EXPORT_COLORS: + if use_colors: file.write('%u %u %u' % v[3]) # col file.write('\n') @@ -207,9 +192,9 @@ def write(filename, scene, ob, \ file.write('4 %d %d %d %d\n' % tuple(pf)) file.close() - print("writing", filename, "done") + print("writing %r done" % filepath) - if EXPORT_APPLY_MODIFIERS: + if use_modifiers: bpy.data.meshes.remove(mesh) # XXX @@ -217,65 +202,5 @@ def write(filename, scene, ob, \ if is_editmode: Blender.Window.EditMode(1, '', 0) """ - -from bpy.props import * -from io_utils import ExportHelper - - -class ExportPLY(bpy.types.Operator, ExportHelper): - '''Export a single object as a stanford PLY with normals, colours and texture coordinates.''' - bl_idname = "export.ply" - bl_label = "Export PLY" - filename_ext = ".ply" - - # List of operator properties, the attributes will be assigned - # to the class instance from the operator settings before calling. - - 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) - use_uvs = BoolProperty(name="UVs", description="Exort the active UV layer", default=True) - use_colors = BoolProperty(name="Vertex Colors", description="Exort the active vertex color layer", default=True) - - @classmethod - def poll(cls, context): - return context.active_object != None - - def execute(self, context): - filepath = self.properties.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - - write(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, - EXPORT_COLORS=self.properties.use_colors, - ) - - return {'FINISHED'} - - def draw(self, context): - layout = self.layout - props = self.properties - - row = layout.row() - row.prop(props, "use_modifiers") - row.prop(props, "use_normals") - row = layout.row() - row.prop(props, "use_uvs") - row.prop(props, "use_colors") - - -def menu_func(self, context): - self.layout.operator(ExportPLY.bl_idname, text="Stanford (.ply)") - - -def register(): - bpy.types.INFO_MT_file_export.append(menu_func) - - -def unregister(): - bpy.types.INFO_MT_file_export.remove(menu_func) - -if __name__ == "__main__": - register() + return {'FINISHED'} diff --git a/release/scripts/op/io_scene_3ds/__init__.py b/release/scripts/op/io_scene_3ds/__init__.py new file mode 100644 index 00000000000..cff8feb7255 --- /dev/null +++ b/release/scripts/op/io_scene_3ds/__init__.py @@ -0,0 +1,86 @@ +# ##### 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> + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + import sys + reload(sys.modules.get("io_scene_3ds.import_3ds", sys)) + reload(sys.modules.get("io_scene_3ds.export_3ds", sys)) + + +import bpy +from bpy.props import * +from io_utils import ImportHelper, ExportHelper + + +class Import3DS(bpy.types.Operator, ImportHelper): + '''Import from 3DS file format (.3ds)''' + bl_idname = "import_scene.autodesk_3ds" + bl_label = 'Import 3DS' + + filename_ext = ".3ds" + + constrain_size = 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) + use_image_search = BoolProperty(name="Image Search", description="Search subdirectories for any assosiated images (Warning, may be slow)", default=True) + use_apply_transform = BoolProperty(name="Apply Transform", description="Workaround for object transformations importing incorrectly", default=False) + + def execute(self, context): + import io_scene_3ds.import_3ds + return io_scene_3ds.import_3ds.load(self, context, **self.properties) + + +class Export3DS(bpy.types.Operator, ExportHelper): + '''Export to 3DS file format (.3ds)''' + bl_idname = "export_scene.autodesk_3ds" + bl_label = 'Export 3DS' + + filename_ext = ".3ds" + + def execute(self, context): + import io_scene_3ds.export_3ds + return io_scene_3ds.export_3ds.save(self, context, **self.properties) + + +# Add to a menu +def menu_func_export(self, context): + self.layout.operator(Export3DS.bl_idname, text="3D Studio (.3ds)") + +def menu_func_import(self, context): + self.layout.operator(Import3DS.bl_idname, text="3D Studio (.3ds)") + +def register(): + bpy.types.INFO_MT_file_import.append(menu_func_import) + bpy.types.INFO_MT_file_export.append(menu_func_export) + + +def unregister(): + bpy.types.INFO_MT_file_import.remove(menu_func_import) + bpy.types.INFO_MT_file_export.remove(menu_func_export) + +if __name__ == "__main__": + register() + +# NOTES: +# why add 1 extra vertex? and remove it when done? - "Answer - eekadoodle - would need to re-order UV's without this since face order isnt always what we give blender, BMesh will solve :D" +# disabled scaling to size, this requires exposing bb (easy) and understanding how it works (needs some time) + +if __name__ == "__main__": + register() + diff --git a/release/scripts/op/io_scene_3ds/export_3ds.py b/release/scripts/op/io_scene_3ds/export_3ds.py index c972ba22178..3d1cc02a8d8 100644 --- a/release/scripts/op/io_scene_3ds/export_3ds.py +++ b/release/scripts/op/io_scene_3ds/export_3ds.py @@ -32,58 +32,81 @@ from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode. #Some of the chunks that we will export #----- Primary Chunk, at the beginning of each file -PRIMARY= int("0x4D4D",16) +PRIMARY= 0x4D4D #------ Main Chunks -OBJECTINFO = int("0x3D3D",16); #This gives the version of the mesh and is found right before the material and object information -VERSION = int("0x0002",16); #This gives the version of the .3ds file -KFDATA = int("0xB000",16); #This is the header for all of the key frame info +OBJECTINFO = 0x3D3D #This gives the version of the mesh and is found right before the material and object information +VERSION = 0x0002 #This gives the version of the .3ds file +KFDATA = 0xB000 #This is the header for all of the key frame info #------ sub defines of OBJECTINFO MATERIAL=45055 #0xAFFF // This stored the texture info OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... #>------ sub defines of MATERIAL -MATNAME = int("0xA000",16); # This holds the material name -MATAMBIENT = int("0xA010",16); # Ambient color of the object/material -MATDIFFUSE = int("0xA020",16); # This holds the color of the object/material -MATSPECULAR = int("0xA030",16); # SPecular color of the object/material -MATSHINESS = int("0xA040",16); # ?? -MATMAP = int("0xA200",16); # This is a header for a new material -MATMAPFILE = int("0xA300",16); # This holds the file name of the texture +MATNAME = 0xA000 # This holds the material name +MATAMBIENT = 0xA010 # Ambient color of the object/material +MATDIFFUSE = 0xA020 # This holds the color of the object/material +MATSPECULAR = 0xA030 # SPecular color of the object/material +MATSHINESS = 0xA040 # ?? +MATMAP = 0xA200 # This is a header for a new material +MATMAPFILE = 0xA300 # This holds the file name of the texture -RGB1= int("0x0011",16) -RGB2= int("0x0012",16) +RGB1= 0x0011 +RGB2= 0x0012 #>------ sub defines of OBJECT -OBJECT_MESH = int("0x4100",16); # This lets us know that we are reading a new object -OBJECT_LIGHT = int("0x4600",16); # This lets un know we are reading a light object -OBJECT_CAMERA= int("0x4700",16); # This lets un know we are reading a camera object +OBJECT_MESH = 0x4100 # This lets us know that we are reading a new object +OBJECT_LIGHT = 0x4600 # This lets un know we are reading a light object +OBJECT_CAMERA= 0x4700 # This lets un know we are reading a camera object #>------ sub defines of CAMERA -OBJECT_CAM_RANGES= int("0x4720",16); # The camera range values +OBJECT_CAM_RANGES= 0x4720 # The camera range values #>------ sub defines of OBJECT_MESH -OBJECT_VERTICES = int("0x4110",16); # The objects vertices -OBJECT_FACES = int("0x4120",16); # The objects faces -OBJECT_MATERIAL = int("0x4130",16); # This is found if the object has a material, either texture map or color -OBJECT_UV = int("0x4140",16); # The UV texture coordinates -OBJECT_TRANS_MATRIX = int("0x4160",16); # The Object Matrix +OBJECT_VERTICES = 0x4110 # The objects vertices +OBJECT_FACES = 0x4120 # The objects faces +OBJECT_MATERIAL = 0x4130 # This is found if the object has a material, either texture map or color +OBJECT_UV = 0x4140 # The UV texture coordinates +OBJECT_TRANS_MATRIX = 0x4160 # The Object Matrix #>------ sub defines of KFDATA -KFDATA_KFHDR = int("0xB00A",16); -KFDATA_KFSEG = int("0xB008",16); -KFDATA_KFCURTIME = int("0xB009",16); -KFDATA_OBJECT_NODE_TAG = int("0xB002",16); +KFDATA_KFHDR = 0xB00A +KFDATA_KFSEG = 0xB008 +KFDATA_KFCURTIME = 0xB009 +KFDATA_OBJECT_NODE_TAG = 0xB002 #>------ sub defines of OBJECT_NODE_TAG -OBJECT_NODE_ID = int("0xB030",16); -OBJECT_NODE_HDR = int("0xB010",16); -OBJECT_PIVOT = int("0xB013",16); -OBJECT_INSTANCE_NAME = int("0xB011",16); -POS_TRACK_TAG = int("0xB020",16); -ROT_TRACK_TAG = int("0xB021",16); -SCL_TRACK_TAG = int("0xB022",16); +OBJECT_NODE_ID = 0xB030 +OBJECT_NODE_HDR = 0xB010 +OBJECT_PIVOT = 0xB013 +OBJECT_INSTANCE_NAME = 0xB011 +POS_TRACK_TAG = 0xB020 +ROT_TRACK_TAG = 0xB021 +SCL_TRACK_TAG = 0xB022 + +import struct + +# So 3ds max can open files, limit names to 12 in length +# this is verry annoying for filenames! +name_unique = [] +name_mapping = {} +def sane_name(name): + name_fixed = name_mapping.get(name) + if name_fixed is not None: + return name_fixed + + new_name = name[:12] + + i = 0 + + while new_name in name_unique: + new_name = new_name[:-4] + '.%.3d' % i + i+=1 + + name_unique.append(new_name) + name_mapping[name] = new_name + return new_name def uv_key(uv): return round(uv[0], 6), round(uv[1], 6) @@ -293,7 +316,7 @@ class _3ds_named_variable(object): if (self.value!=None): spaces="" for i in range(indent): - spaces+=" "; + spaces += " " if (self.name!=""): print(spaces, self.name, " = ", self.value) else: @@ -358,7 +381,7 @@ class _3ds_chunk(object): Uses the dump function of the named variables and the subchunks to do the actual work.''' spaces="" for i in range(indent): - spaces+=" "; + spaces += " " print(spaces, "ID=", hex(self.ID.value), "size=", self.get_size()) for variable in self.variables: variable.dump(indent+1) @@ -393,11 +416,11 @@ def make_material_subchunk(id, color): Used for color subchunks, such as diffuse color or ambient color subchunks.''' mat_sub = _3ds_chunk(id) col1 = _3ds_chunk(RGB1) - col1.add_variable("color1", _3ds_rgb_color(color)); + col1.add_variable("color1", _3ds_rgb_color(color)) mat_sub.add_subchunk(col1) # optional: # col2 = _3ds_chunk(RGB1) -# col2.add_variable("color2", _3ds_rgb_color(color)); +# col2.add_variable("color2", _3ds_rgb_color(color)) # mat_sub.add_subchunk(col2) return mat_sub @@ -835,21 +858,21 @@ def make_kf_obj_node(obj, name_to_id): return kf_obj_node """ -# import BPyMessages -def write(filename, context): + +def save(operator, context, filepath=""): + import bpy + import time + from io_utils import create_derived_objects, free_derived_objects + '''Save the Blender scene to a 3ds file.''' + # Time the export - - # XXX -# if not BPyMessages.Warning_SaveOver(filename): -# return - time1 = time.clock() # Blender.Window.WaitCursor(1) sce = context.scene - if context.object: + if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') # Initialize the main chunk (primary): @@ -998,7 +1021,7 @@ def write(filename, context): # Check the size: primary.get_size() # Open the file for writing: - file = open( filename, 'wb' ) + file = open(filepath, 'wb') # Recursively write the chunks to file: primary.write(file) @@ -1006,50 +1029,15 @@ def write(filename, context): # Close the file: file.close() + # Clear name mapping vars, could make locals too + name_unique[:] = [] + name_mapping.clear() + # Debugging only: report the exporting time: # Blender.Window.WaitCursor(0) print("3ds export time: %.2f" % (time.clock() - time1)) -# print("3ds export time: %.2f" % (Blender.sys.time() - time1)) # Debugging only: dump the chunk hierarchy: #primary.dump() - -import bpy -from bpy.props import * -from io_utils import ExportHelper -from io_utils import create_derived_objects, free_derived_objects - - -class Export3DS(bpy.types.Operator, ExportHelper): - '''Export to 3DS file format (.3ds)''' - bl_idname = "export.autodesk_3ds" - bl_label = 'Export 3DS' - filename_ext = ".3ds" - - @classmethod - def poll(cls, context): # Poll isnt working yet - return context.active_object != None - - def execute(self, context): - filepath = self.properties.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - - write(filepath, context) - return {'FINISHED'} - - -# Add to a menu -def menu_func(self, context): - self.layout.operator(Export3DS.bl_idname, text="3D Studio (.3ds)") - - -def register(): - bpy.types.INFO_MT_file_export.append(menu_func) - - -def unregister(): - bpy.types.INFO_MT_file_export.remove(menu_func) - -if __name__ == "__main__": - register() + return {'FINISHED'} diff --git a/release/scripts/op/io_scene_3ds/import_3ds.py b/release/scripts/op/io_scene_3ds/import_3ds.py index 37f707af3e3..5ea705f15f7 100644 --- a/release/scripts/op/io_scene_3ds/import_3ds.py +++ b/release/scripts/op/io_scene_3ds/import_3ds.py @@ -25,7 +25,7 @@ import os import time import struct -from import_scene_obj import load_image +from io_utils import load_image import bpy import mathutils @@ -42,9 +42,9 @@ BOUNDS_3DS = [] PRIMARY = int('0x4D4D',16) #------ Main Chunks -OBJECTINFO = int('0x3D3D',16); #This gives the version of the mesh and is found right before the material and object information -VERSION = int('0x0002',16); #This gives the version of the .3ds file -EDITKEYFRAME= int('0xB000',16); #This is the header for all of the key frame info +OBJECTINFO = 0x3D3D #This gives the version of the mesh and is found right before the material and object information +VERSION = 0x0002 #This gives the version of the .3ds file +EDITKEYFRAME= 0xB000 #This is the header for all of the key frame info #------ sub defines of OBJECTINFO MATERIAL = 45055 #0xAFFF // This stored the texture info @@ -52,62 +52,62 @@ OBJECT = 16384 #0x4000 // This stores the faces, vertices, etc... #>------ sub defines of MATERIAL #------ sub defines of MATERIAL_BLOCK -MAT_NAME = int('0xA000',16) # This holds the material name -MAT_AMBIENT = int('0xA010',16) # Ambient color of the object/material -MAT_DIFFUSE = int('0xA020',16) # This holds the color of the object/material -MAT_SPECULAR = int('0xA030',16) # SPecular color of the object/material -MAT_SHINESS = int('0xA040',16) # ?? -MAT_TRANSPARENCY= int('0xA050',16) # Transparency value of material -MAT_SELF_ILLUM = int('0xA080',16) # Self Illumination value of material -MAT_WIRE = int('0xA085',16) # Only render's wireframe - -MAT_TEXTURE_MAP = int('0xA200',16) # This is a header for a new texture map -MAT_SPECULAR_MAP= int('0xA204',16) # This is a header for a new specular map -MAT_OPACITY_MAP = int('0xA210',16) # This is a header for a new opacity map -MAT_REFLECTION_MAP= int('0xA220',16) # This is a header for a new reflection map -MAT_BUMP_MAP = int('0xA230',16) # This is a header for a new bump map -MAT_MAP_FILENAME = int('0xA300',16) # This holds the file name of the texture - -MAT_FLOAT_COLOR = int ('0x0010', 16) #color defined as 3 floats -MAT_24BIT_COLOR = int ('0x0011', 16) #color defined as 3 bytes +MAT_NAME = 0xA000 # This holds the material name +MAT_AMBIENT = 0xA010 # Ambient color of the object/material +MAT_DIFFUSE = 0xA020 # This holds the color of the object/material +MAT_SPECULAR = 0xA030 # SPecular color of the object/material +MAT_SHINESS = 0xA040 # ?? +MAT_TRANSPARENCY= 0xA050 # Transparency value of material +MAT_SELF_ILLUM = 0xA080 # Self Illumination value of material +MAT_WIRE = 0xA085 # Only render's wireframe + +MAT_TEXTURE_MAP = 0xA200 # This is a header for a new texture map +MAT_SPECULAR_MAP= 0xA204 # This is a header for a new specular map +MAT_OPACITY_MAP = 0xA210 # This is a header for a new opacity map +MAT_REFLECTION_MAP= 0xA220 # This is a header for a new reflection map +MAT_BUMP_MAP = 0xA230 # This is a header for a new bump map +MAT_MAP_FILEPATH = 0xA300 # This holds the file name of the texture + +MAT_FLOAT_COLOR = 0x0010 #color defined as 3 floats +MAT_24BIT_COLOR = 0x0011 #color defined as 3 bytes #>------ sub defines of OBJECT -OBJECT_MESH = int('0x4100',16); # This lets us know that we are reading a new object -OBJECT_LAMP = int('0x4600',16); # This lets un know we are reading a light object -OBJECT_LAMP_SPOT = int('0x4610',16); # The light is a spotloght. -OBJECT_LAMP_OFF = int('0x4620',16); # The light off. -OBJECT_LAMP_ATTENUATE = int('0x4625',16); -OBJECT_LAMP_RAYSHADE = int('0x4627',16); -OBJECT_LAMP_SHADOWED = int('0x4630',16); -OBJECT_LAMP_LOCAL_SHADOW = int('0x4640',16); -OBJECT_LAMP_LOCAL_SHADOW2 = int('0x4641',16); -OBJECT_LAMP_SEE_CONE = int('0x4650',16); -OBJECT_LAMP_SPOT_RECTANGULAR = int('0x4651',16); -OBJECT_LAMP_SPOT_OVERSHOOT = int('0x4652',16); -OBJECT_LAMP_SPOT_PROJECTOR = int('0x4653',16); -OBJECT_LAMP_EXCLUDE = int('0x4654',16); -OBJECT_LAMP_RANGE = int('0x4655',16); -OBJECT_LAMP_ROLL = int('0x4656',16); -OBJECT_LAMP_SPOT_ASPECT = int('0x4657',16); -OBJECT_LAMP_RAY_BIAS = int('0x4658',16); -OBJECT_LAMP_INNER_RANGE = int('0x4659',16); -OBJECT_LAMP_OUTER_RANGE = int('0x465A',16); -OBJECT_LAMP_MULTIPLIER = int('0x465B',16); -OBJECT_LAMP_AMBIENT_LIGHT = int('0x4680',16); - - - -OBJECT_CAMERA= int('0x4700',16); # This lets un know we are reading a camera object +OBJECT_MESH = 0x4100 # This lets us know that we are reading a new object +OBJECT_LAMP = 0x4600 # This lets un know we are reading a light object +OBJECT_LAMP_SPOT = 0x4610 # The light is a spotloght. +OBJECT_LAMP_OFF = 0x4620 # The light off. +OBJECT_LAMP_ATTENUATE = 0x4625 +OBJECT_LAMP_RAYSHADE = 0x4627 +OBJECT_LAMP_SHADOWED = 0x4630 +OBJECT_LAMP_LOCAL_SHADOW = 0x4640 +OBJECT_LAMP_LOCAL_SHADOW2 = 0x4641 +OBJECT_LAMP_SEE_CONE = 0x4650 +OBJECT_LAMP_SPOT_RECTANGULAR = 0x4651 +OBJECT_LAMP_SPOT_OVERSHOOT = 0x4652 +OBJECT_LAMP_SPOT_PROJECTOR = 0x4653 +OBJECT_LAMP_EXCLUDE = 0x4654 +OBJECT_LAMP_RANGE = 0x4655 +OBJECT_LAMP_ROLL = 0x4656 +OBJECT_LAMP_SPOT_ASPECT = 0x4657 +OBJECT_LAMP_RAY_BIAS = 0x4658 +OBJECT_LAMP_INNER_RANGE = 0x4659 +OBJECT_LAMP_OUTER_RANGE = 0x465A +OBJECT_LAMP_MULTIPLIER = 0x465B +OBJECT_LAMP_AMBIENT_LIGHT = 0x4680 + + + +OBJECT_CAMERA= 0x4700 # This lets un know we are reading a camera object #>------ sub defines of CAMERA -OBJECT_CAM_RANGES= int('0x4720',16); # The camera range values +OBJECT_CAM_RANGES= 0x4720 # The camera range values #>------ sub defines of OBJECT_MESH -OBJECT_VERTICES = int('0x4110',16); # The objects vertices -OBJECT_FACES = int('0x4120',16); # The objects faces -OBJECT_MATERIAL = int('0x4130',16); # This is found if the object has a material, either texture map or color -OBJECT_UV = int('0x4140',16); # The UV texture coordinates -OBJECT_TRANS_MATRIX = int('0x4160',16); # The Object Matrix +OBJECT_VERTICES = 0x4110 # The objects vertices +OBJECT_FACES = 0x4120 # The objects faces +OBJECT_MATERIAL = 0x4130 # This is found if the object has a material, either texture map or color +OBJECT_UV = 0x4140 # The UV texture coordinates +OBJECT_TRANS_MATRIX = 0x4160 # The Object Matrix global scn scn = None @@ -304,7 +304,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): #print 'MAT_TEXTURE_MAP..while', new_chunk.bytes_read, new_chunk.length read_chunk(file, temp_chunk) - if (temp_chunk.ID == MAT_MAP_FILENAME): + if (temp_chunk.ID == MAT_MAP_FILEPATH): texture_name = read_string(file) img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname) new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed @@ -318,7 +318,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): if img: add_texture_to_material(img, new_texture, contextMaterial, mapto) - dirname = os.path.dirname(FILENAME) + dirname = os.path.dirname(file.name) #loop through all the data for this chunk (previous chunk) and see what it is while (previous_chunk.bytes_read < previous_chunk.length): @@ -604,14 +604,14 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): #contextMatrix = contextMatrix * tx #contextMatrix = contextMatrix *tx - elif (new_chunk.ID == MAT_MAP_FILENAME): + elif (new_chunk.ID == MAT_MAP_FILEPATH): texture_name = read_string(file) try: TEXTURE_DICT[contextMaterial.name] except: - #img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME) + #img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILEPATH) img = TEXTURE_DICT[contextMaterial.name] = load_image(texture_name, dirname) -# img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILENAME, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH) +# img = TEXTURE_DICT[contextMaterial.name]= BPyImage.comprehensiveImageLoad(texture_name, FILEPATH, PLACE_HOLDER=False, RECURSIVE=IMAGE_SEARCH) new_chunk.bytes_read += len(texture_name)+1 #plus one for the null character that gets removed @@ -634,30 +634,27 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): if CreateBlenderObject: putContextMesh(contextMesh_vertls, contextMesh_facels, contextMeshMaterials) -def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, APPLY_MATRIX=False): - global FILENAME, SCN -# global FILENAME, SCN_OBJECTS +def load_3ds(filepath, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, APPLY_MATRIX=False): + global SCN # XXX -# if BPyMessages.Error_NoFile(filename): +# if BPyMessages.Error_NoFile(filepath): # return - print('\n\nImporting 3DS: "%s"' % (filename)) -# print('\n\nImporting 3DS: "%s"' % (Blender.sys.expandpath(filename))) + print('\n\nImporting 3DS: %r' % (filepath)) time1 = time.clock() # time1 = Blender.sys.time() - FILENAME = filename current_chunk = chunk() - file = open(filename,'rb') + file = open(filepath, 'rb') #here we go! # print 'reading the first chunk' read_chunk(file, current_chunk) if (current_chunk.ID!=PRIMARY): - print('\tFatal Error: Not a valid 3ds file: ', filename) + print('\tFatal Error: Not a valid 3ds file: %r' % filepath) file.close() return @@ -718,7 +715,7 @@ def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, # Done DUMMYVERT """ if IMPORT_AS_INSTANCE: - name = filename.split('\\')[-1].split('/')[-1] + name = filepath.split('\\')[-1].split('/')[-1] # Create a group for this import. group_scn = Scene.New(name) for ob in importedObjects: @@ -776,90 +773,10 @@ def load_3ds(filename, context, IMPORT_CONSTRAIN_BOUNDS=10.0, IMAGE_SEARCH=True, # Done constraining to bounds. # Select all new objects. - print('finished importing: "%s" in %.4f sec.' % (filename, (time.clock()-time1))) -# print('finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))) + print('finished importing: %r in %.4f sec.' % (filepath, (time.clock()-time1))) file.close() -DEBUG = False -# For testing compatibility -#load_3ds('/metavr/convert/vehicle/truck_002/TruckTanker1.3DS', False) -#load_3ds('/metavr/archive/convert/old/arranged_3ds_to_hpx-2/only-need-engine-trains/Engine2.3DS', False) -''' - -else: - import os - # DEBUG ONLY - TIME = Blender.sys.time() - import os - print 'Searching for files' - os.system('find /metavr/ -iname "*.3ds" > /tmp/temp3ds_list') - # os.system('find /storage/ -iname "*.3ds" > /tmp/temp3ds_list') - print '...Done' - file = open('/tmp/temp3ds_list', 'r') - lines = file.readlines() - file.close() - # sort by filesize for faster testing - lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] - lines_size.sort() - lines = [f[1] for f in lines_size] - - - def between(v,a,b): - if v <= max(a,b) and v >= min(a,b): - return True - return False - - for i, _3ds in enumerate(lines): - if between(i, 650,800): - #_3ds= _3ds[:-1] - print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines) - _3ds_file= _3ds.split('/')[-1].split('\\')[-1] - newScn = Blender.Scene.New(_3ds_file) - newScn.makeCurrent() - load_3ds(_3ds, False) - - print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) - -''' -from bpy.props import * -from io_utils import ImportHelper - - -class IMPORT_OT_autodesk_3ds(bpy.types.Operator, ImportHelper): - '''Import from 3DS file format (.3ds)''' - bl_idname = "import_scene.autodesk_3ds" - bl_label = 'Import 3DS' - - filename_ext = ".3ds" - - constrain_size = 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_transform = BoolProperty(name="Apply Transform", description="Workaround for object transformations importing incorrectly", default=False) - - def execute(self, context): - load_3ds(self.properties.filepath, - context, - IMPORT_CONSTRAIN_BOUNDS=self.properties.constrain_size, - IMAGE_SEARCH=self.properties.search_images, - APPLY_MATRIX=self.properties.apply_transform) - - return {'FINISHED'} - - -def menu_func(self, context): - self.layout.operator(IMPORT_OT_autodesk_3ds.bl_idname, text="3D Studio (.3ds)") - -def register(): - bpy.types.INFO_MT_file_import.append(menu_func) - -def unregister(): - bpy.types.INFO_MT_file_import.remove(menu_func) - -# NOTES: -# why add 1 extra vertex? and remove it when done? - "Answer - eekadoodle - would need to re-order UV's without this since face order isnt always what we give blender, BMesh will solve :D" -# disabled scaling to size, this requires exposing bb (easy) and understanding how it works (needs some time) - -if __name__ == "__main__": - register() - +def load(operator, context, filepath="", constrain_size=0.0, use_image_search=True, use_apply_transform=True): + load_3ds(filepath, context, IMPORT_CONSTRAIN_BOUNDS=constrain_size, IMAGE_SEARCH=use_image_search, APPLY_MATRIX=use_apply_transform) + return {'FINISHED'} diff --git a/release/scripts/op/io_scene_fbx/__init__.py b/release/scripts/op/io_scene_fbx/__init__.py new file mode 100644 index 00000000000..f7195600de5 --- /dev/null +++ b/release/scripts/op/io_scene_fbx/__init__.py @@ -0,0 +1,102 @@ +# ##### 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> + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + # only reload if we alredy loaded, highly annoying + import sys + reload(sys.modules.get("io_scene_fbx.export_fbx", sys)) + + +import bpy +from bpy.props import * +from io_utils import ExportHelper + + +class ExportFBX(bpy.types.Operator, ExportHelper): + '''Selection to an ASCII Autodesk FBX''' + bl_idname = "export_scene.fbx" + bl_label = "Export FBX" + + filename_ext = ".fbx" + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + EXP_OBS_SELECTED = BoolProperty(name="Selected Objects", description="Export selected objects on visible layers", default=True) +# EXP_OBS_SCENE = BoolProperty(name="Scene Objects", description="Export all objects in this scene", default=True) + TX_SCALE = FloatProperty(name="Scale", description="Scale all data, (Note! some imports dont support scaled armatures)", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0) + TX_XROT90 = BoolProperty(name="Rot X90", description="Rotate all objects 90 degrees about the X axis", default=True) + TX_YROT90 = BoolProperty(name="Rot Y90", description="Rotate all objects 90 degrees about the Y axis", default=False) + TX_ZROT90 = BoolProperty(name="Rot Z90", description="Rotate all objects 90 degrees about the Z axis", default=False) + EXP_EMPTY = BoolProperty(name="Empties", description="Export empty objects", default=True) + EXP_CAMERA = BoolProperty(name="Cameras", description="Export camera objects", default=True) + EXP_LAMP = BoolProperty(name="Lamps", description="Export lamp objects", default=True) + EXP_ARMATURE = BoolProperty(name="Armatures", description="Export armature objects", default=True) + EXP_MESH = BoolProperty(name="Meshes", description="Export mesh objects", default=True) + EXP_MESH_APPLY_MOD = BoolProperty(name="Modifiers", description="Apply modifiers to mesh objects", default=True) + EXP_MESH_HQ_NORMALS = BoolProperty(name="HQ Normals", description="Generate high quality normals", default=True) + EXP_IMAGE_COPY = BoolProperty(name="Copy Image Files", description="Copy image files to the destination path", default=False) + # armature animation + ANIM_ENABLE = BoolProperty(name="Enable Animation", description="Export keyframe animation", default=True) + ANIM_OPTIMIZE = BoolProperty(name="Optimize Keyframes", description="Remove double keyframes", default=True) + ANIM_OPTIMIZE_PRECISSION = FloatProperty(name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0) +# ANIM_ACTION_ALL = BoolProperty(name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True) + ANIM_ACTION_ALL = BoolProperty(name="All Actions", description="Use all actions for armatures, if false, use current action", default=False) + # batch + BATCH_ENABLE = BoolProperty(name="Enable Batch", description="Automate exporting multiple scenes or groups to files", default=False) + BATCH_GROUP = BoolProperty(name="Group > File", description="Export each group as an FBX file, if false, export each scene as an FBX file", default=False) + BATCH_OWN_DIR = BoolProperty(name="Own Dir", description="Create a dir for each exported file", default=True) + BATCH_FILE_PREFIX = StringProperty(name="Prefix", description="Prefix each file with this name", maxlen=1024, default="") + + + def execute(self, context): + import math + from mathutils import Matrix + if not self.properties.filepath: + raise Exception("filepath not set") + + mtx4_x90n = Matrix.Rotation(-math.pi/2.0, 4, 'X') + mtx4_y90n = Matrix.Rotation(-math.pi/2.0, 4, 'Y') + mtx4_z90n = Matrix.Rotation(-math.pi/2.0, 4, 'Z') + + GLOBAL_MATRIX = Matrix() + GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self.properties.TX_SCALE + 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 + + import io_scene_fbx.export_fbx + return io_scene_fbx.export_fbx.save(self, context, GLOBAL_MATRIX=GLOBAL_MATRIX, **self.properties) + + +def menu_func(self, context): + self.layout.operator(ExportFBX.bl_idname, text="Autodesk FBX (.fbx)") + + +def register(): + bpy.types.INFO_MT_file_export.append(menu_func) + + +def unregister(): + bpy.types.INFO_MT_file_export.remove(menu_func) + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/io_scene_fbx/export_fbx.py b/release/scripts/op/io_scene_fbx/export_fbx.py index c48e86c2519..ec13decc026 100644 --- a/release/scripts/op/io_scene_fbx/export_fbx.py +++ b/release/scripts/op/io_scene_fbx/export_fbx.py @@ -34,19 +34,10 @@ import shutil # for file copying import bpy from mathutils import Vector, Euler, Matrix -def copy_file(source, dest): - # XXX - remove, can use shutil - file = open(source, 'rb') - data = file.read() - file.close() - - file = open(dest, 'wb') - file.write(data) - file.close() - - # XXX not used anymore, images are copied one at a time def copy_images(dest_dir, textures): + import shutil + if not dest_dir.endswith(os.sep): dest_dir += os.sep @@ -61,12 +52,12 @@ def copy_images(dest_dir, textures): # Make a name for the target path. dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] if not Blender.sys.exists(dest_image_path): # Image isnt already there - print('\tCopying "%s" > "%s"' % (image_path, dest_image_path)) + print("\tCopying %r > %r" % (image_path, dest_image_path)) try: - copy_file(image_path, dest_image_path) + shutil.copy(image_path, dest_image_path) copyCount+=1 except: - print('\t\tWarning, file failed to copy, skipping.') + print("\t\tWarning, file failed to copy, skipping.") print('\tCopied %d images' % copyCount) @@ -81,27 +72,11 @@ def eulerRadToDeg(eul): return ret -mtx4_identity = Matrix() - -# testing -mtx_x90 = Matrix.Rotation( math.pi/2, 3, 'X') # used -#mtx_x90n = Matrix.Rotation(-90, 3, 'x') -#mtx_y90 = Matrix.Rotation( 90, 3, 'y') -#mtx_y90n = Matrix.Rotation(-90, 3, 'y') -#mtx_z90 = Matrix.Rotation( 90, 3, 'z') -#mtx_z90n = Matrix.Rotation(-90, 3, 'z') - -#mtx4_x90 = Matrix.Rotation( 90, 4, 'x') -mtx4_x90n = Matrix.Rotation(-math.pi/2, 4, 'X') # used -#mtx4_y90 = Matrix.Rotation( 90, 4, 'y') -mtx4_y90n = Matrix.Rotation(-math.pi/2, 4, 'Y') # used -mtx4_z90 = Matrix.Rotation( math.pi/2, 4, 'Z') # used -mtx4_z90n = Matrix.Rotation(-math.pi/2, 4, 'Z') # used # def strip_path(p): # return p.split('\\')[-1].split('/')[-1] -# Used to add the scene name into the filename without using odd chars +# Used to add the scene name into the filepath without using odd chars sane_name_mapping_ob = {} sane_name_mapping_mat = {} sane_name_mapping_tex = {} @@ -174,7 +149,7 @@ def sane_groupname(data): return sane_name(data, sane_name_mapping_group) # ''' # fname_orig - blender path, can be relative # basepath - fname_rel will be relative to this -# FORCE_CWD - dont use the basepath, just add a ./ to the filename. +# FORCE_CWD - dont use the basepath, just add a ./ to the filepath. # use when we know the file will be in the basepath. # ''' # fname = bpy.path.abspath(fname_orig) @@ -259,19 +234,17 @@ header_comment = \ ''' -# This func can be called with just the filename -def write(filename, batch_objects = None, \ - context = None, +# This func can be called with just the filepath +def save(operator, context, filepath="", \ EXP_OBS_SELECTED = True, EXP_MESH = True, EXP_MESH_APPLY_MOD = True, -# EXP_MESH_HQ_NORMALS = False, EXP_ARMATURE = True, EXP_LAMP = True, EXP_CAMERA = True, EXP_EMPTY = True, EXP_IMAGE_COPY = False, - GLOBAL_MATRIX = Matrix(), + GLOBAL_MATRIX = None, ANIM_ENABLE = True, ANIM_OPTIMIZE = True, ANIM_OPTIMIZE_PRECISSION = 6, @@ -282,16 +255,26 @@ def write(filename, batch_objects = None, \ BATCH_OWN_DIR = False ): - if bpy.context.object: + #XXX, missing arg + batch_objects = None + + # testing + mtx_x90 = Matrix.Rotation( math.pi/2.0, 3, 'X') # used + mtx4_z90 = Matrix.Rotation( math.pi/2.0, 4, 'Z') + + if GLOBAL_MATRIX is None: + GLOBAL_MATRIX = Matrix() + + if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') # ----------------- Batch support! if BATCH_ENABLE: if os == None: BATCH_OWN_DIR = False - fbxpath = filename + fbxpath = filepath - # get the path component of filename + # get the path component of filepath tmp_exists = bpy.utils.exists(fbxpath) # tmp_exists = Blender.sys.exists(fbxpath) @@ -300,7 +283,7 @@ def write(filename, batch_objects = None, \ # while fbxpath and fbxpath[-1] not in ('/', '\\'): # fbxpath = fbxpath[:-1] if not fbxpath: -# if not filename: +# if not filepath: # XXX print('Error%t|Directory does not exist!') # Draw.PupMenu('Error%t|Directory does not exist!') @@ -345,9 +328,9 @@ def write(filename, batch_objects = None, \ os.mkdir(new_fbxpath) - filename = new_fbxpath + newname + '.fbx' + filepath = new_fbxpath + newname + '.fbx' - print('\nBatch exporting %s as...\n\t"%s"' % (data, filename)) + print('\nBatch exporting %s as...\n\t%r' % (data, filepath)) # XXX don't know what to do with this, probably do the same? (Arystan) if BATCH_GROUP: #group @@ -370,12 +353,11 @@ def write(filename, batch_objects = None, \ # Call self with modified args # Dont pass batch options since we already usedt them - write(filename, data.objects, + write(filepath, data.objects, context, False, EXP_MESH, EXP_MESH_APPLY_MOD, -# EXP_MESH_HQ_NORMALS, EXP_ARMATURE, EXP_LAMP, EXP_CAMERA, @@ -400,9 +382,9 @@ def write(filename, batch_objects = None, \ # end batch support # Use this for working out paths relative to the export location - basepath = os.path.dirname(filename) or '.' + basepath = os.path.dirname(filepath) or '.' basepath += os.sep -# basepath = Blender.sys.dirname(filename) +# basepath = Blender.sys.dirname(filepath) # ---------------------------------------------- # storage classes @@ -549,11 +531,11 @@ def write(filename, batch_objects = None, \ - print('\nFBX export starting...', filename) + print('\nFBX export starting... %r' % filepath) start_time = time.clock() # start_time = Blender.sys.time() try: - file = open(filename, 'w') + file = open(filepath, 'w') except: return False @@ -2449,7 +2431,7 @@ Objects: {''') file.write('\n\t\tPoseNode: {') file.write('\n\t\t\tNode: "Model::%s"' % fbxName ) if matrix: file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix)) - else: file.write('\n\t\t\tMatrix: %s' % mat4x4str(mtx4_identity)) + else: file.write('\n\t\t\tMatrix: %s' % mat4x4str(Matrix())) file.write('\n\t\t}') file.write('\n\t}') @@ -2946,12 +2928,10 @@ Takes: {''') mist_start = m.start mist_end = m.depth mist_height = m.height -# mist_intense, mist_start, mist_end, mist_height = world.mist world_hor = world.horizon_color -# world_hor = world.hor else: has_mist = mist_intense = mist_start = mist_end = mist_height = 0 - world_hor = 0,0,0 + world_hor = 0, 0, 0 file.write('\n;Version 5 settings') file.write('\n;------------------------------------------------------------------') @@ -3003,94 +2983,7 @@ Takes: {''') # bpy.util.copy_images( [ tex[1] for tex in textures if tex[1] != None ], basepath) print('export finished in %.4f sec.' % (time.clock() - start_time)) - return True - -from bpy.props import * -from io_utils import ExportHelper - - -class ExportFBX(bpy.types.Operator, ExportHelper): - '''Selection to an ASCII Autodesk FBX''' - bl_idname = "export.fbx" - bl_label = "Export FBX" - - filename_ext = ".fbx" - - # List of operator properties, the attributes will be assigned - # to the class instance from the operator settings before calling. - - EXP_OBS_SELECTED = BoolProperty(name="Selected Objects", description="Export selected objects on visible layers", default=True) -# EXP_OBS_SCENE = BoolProperty(name="Scene Objects", description="Export all objects in this scene", default=True) - TX_SCALE = FloatProperty(name="Scale", description="Scale all data, (Note! some imports dont support scaled armatures)", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0) - TX_XROT90 = BoolProperty(name="Rot X90", description="Rotate all objects 90 degrees about the X axis", default=True) - TX_YROT90 = BoolProperty(name="Rot Y90", description="Rotate all objects 90 degrees about the Y axis", default=False) - TX_ZROT90 = BoolProperty(name="Rot Z90", description="Rotate all objects 90 degrees about the Z axis", default=False) - EXP_EMPTY = BoolProperty(name="Empties", description="Export empty objects", default=True) - EXP_CAMERA = BoolProperty(name="Cameras", description="Export camera objects", default=True) - EXP_LAMP = BoolProperty(name="Lamps", description="Export lamp objects", default=True) - EXP_ARMATURE = BoolProperty(name="Armatures", description="Export armature objects", default=True) - EXP_MESH = BoolProperty(name="Meshes", description="Export mesh objects", default=True) - EXP_MESH_APPLY_MOD = BoolProperty(name="Modifiers", description="Apply modifiers to mesh objects", default=True) - EXP_MESH_HQ_NORMALS = BoolProperty(name="HQ Normals", description="Generate high quality normals", default=True) - EXP_IMAGE_COPY = BoolProperty(name="Copy Image Files", description="Copy image files to the destination path", default=False) - # armature animation - ANIM_ENABLE = BoolProperty(name="Enable Animation", description="Export keyframe animation", default=True) - ANIM_OPTIMIZE = BoolProperty(name="Optimize Keyframes", description="Remove double keyframes", default=True) - ANIM_OPTIMIZE_PRECISSION = FloatProperty(name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0) -# ANIM_ACTION_ALL = BoolProperty(name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True) - ANIM_ACTION_ALL = BoolProperty(name="All Actions", description="Use all actions for armatures, if false, use current action", default=False) - # batch - BATCH_ENABLE = BoolProperty(name="Enable Batch", description="Automate exporting multiple scenes or groups to files", default=False) - BATCH_GROUP = BoolProperty(name="Group > File", description="Export each group as an FBX file, if false, export each scene as an FBX file", default=False) - BATCH_OWN_DIR = BoolProperty(name="Own Dir", description="Create a dir for each exported file", default=True) - BATCH_FILE_PREFIX = StringProperty(name="Prefix", description="Prefix each file with this name", maxlen=1024, default="") - - - @classmethod - def poll(cls, context): - return context.active_object - - def execute(self, context): - if not self.properties.filepath: - raise Exception("filepath not set") - - filepath = self.properties.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - - 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 = 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(filepath, - None, # XXX - context, - self.properties.EXP_OBS_SELECTED, - self.properties.EXP_MESH, - self.properties.EXP_MESH_APPLY_MOD, -# self.properties.EXP_MESH_HQ_NORMALS, - self.properties.EXP_ARMATURE, - self.properties.EXP_LAMP, - self.properties.EXP_CAMERA, - self.properties.EXP_EMPTY, - self.properties.EXP_IMAGE_COPY, - GLOBAL_MATRIX, - self.properties.ANIM_ENABLE, - self.properties.ANIM_OPTIMIZE, - self.properties.ANIM_OPTIMIZE_PRECISSION, - self.properties.ANIM_ACTION_ALL, - self.properties.BATCH_ENABLE, - self.properties.BATCH_GROUP, - self.properties.BATCH_FILE_PREFIX, - self.properties.BATCH_OWN_DIR, - ) - - return {'FINISHED'} - - -# if __name__ == "__main__": -# bpy.ops.EXPORT_OT_ply(filepath="/tmp/test.ply") + return {'FINISHED'} # NOTES (all line numbers correspond to original export_fbx.py (under release/scripts) @@ -3111,21 +3004,3 @@ class ExportFBX(bpy.types.Operator, ExportHelper): # - bpy.sys.time move to bpy.sys.util? # - new scene creation, activation: lines 327-342, 368 # - uses bpy.path.abspath, *.relpath - replace at least relpath - -# SMALL or COSMETICAL -# - find a way to get blender version, and put it in bpy.util?, old was Blender.Get('version') - - -def menu_func(self, context): - self.layout.operator(ExportFBX.bl_idname, text="Autodesk FBX (.fbx)") - - -def register(): - bpy.types.INFO_MT_file_export.append(menu_func) - - -def unregister(): - bpy.types.INFO_MT_file_export.remove(menu_func) - -if __name__ == "__main__": - register() diff --git a/release/scripts/op/io_scene_obj/__init__.py b/release/scripts/op/io_scene_obj/__init__.py new file mode 100644 index 00000000000..d3791d1cd95 --- /dev/null +++ b/release/scripts/op/io_scene_obj/__init__.py @@ -0,0 +1,144 @@ +# ##### 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> + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + # only reload if we alredy loaded, highly annoying + import sys + reload(sys.modules.get("io_scene_obj.import_obj", sys)) + reload(sys.modules.get("io_scene_obj.export_obj", sys)) + + +import bpy +from bpy.props import * +from io_utils import ExportHelper, ImportHelper + + +class ImportOBJ(bpy.types.Operator, ImportHelper): + '''Load a Wavefront OBJ File''' + bl_idname = "import_scene.obj" + bl_label = "Import OBJ" + + filename_ext = ".obj" + + 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) + # 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) + ROTATE_X90 = BoolProperty(name="-X90", description="Rotate X 90.", default= True) + CLAMP_SIZE = FloatProperty(name="Clamp Scale", description="Clamp the size to this maximum (Zero to Disable)", min=0.0, max=1000.0, soft_min=0.0, soft_max=1000.0, default=0.0) + POLYGROUPS = BoolProperty(name="Poly Groups", description="Import OBJ groups as vertex groups.", default= True) + IMAGE_SEARCH = BoolProperty(name="Image Search", description="Search subdirs for any assosiated images (Warning, may be slow)", default= True) + + + def execute(self, context): + # print("Selected: " + context.active_object.name) + import io_scene_obj.import_obj + return io_scene_obj.import_obj.load(self, context, **self.properties) + ''' + load_obj(self.properties.filepath, + context, + self.properties.CLAMP_SIZE, + self.properties.CREATE_FGONS, + self.properties.CREATE_SMOOTH_GROUPS, + self.properties.CREATE_EDGES, + self.properties.SPLIT_OBJECTS, + self.properties.SPLIT_GROUPS, + self.properties.ROTATE_X90, + self.properties.IMAGE_SEARCH, + self.properties.POLYGROUPS) + ''' + + return {'FINISHED'} + + +class ExportOBJ(bpy.types.Operator, ExportHelper): + '''Save a Wavefront OBJ File''' + + bl_idname = "export_scene.obj" + bl_label = 'Export OBJ' + + filename_ext = ".obj" + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + # context group + use_selection = BoolProperty(name="Selection Only", description="Export selected objects only", default= False) + use_all_scenes = BoolProperty(name="All Scenes", description="", default= False) + use_animation = BoolProperty(name="Animation", description="", default= False) + + # object group + use_modifiers = BoolProperty(name="Apply Modifiers", description="Apply modifiers (preview resolution)", default= True) + use_rotate_x90 = BoolProperty(name="Rotate X90", description="", default= True) + + # extra data group + use_edges = BoolProperty(name="Edges", description="", default=True) + use_normals = BoolProperty(name="Normals", description="", default=False) + use_hq_normals = BoolProperty(name="High Quality Normals", description="", default=True) + use_uvs = BoolProperty(name="UVs", description="", default= True) + use_materials = BoolProperty(name="Materials", description="", default=True) + copy_images = BoolProperty(name="Copy Images", description="", default=False) + use_triangles = BoolProperty(name="Triangulate", description="", default=False) + use_vertex_groups = BoolProperty(name="Polygroups", description="", default=False) + use_nurbs = BoolProperty(name="Nurbs", description="", default=False) + + # grouping group + use_blen_objects = BoolProperty(name="Objects as OBJ Objects", description="", default= True) + group_by_object = BoolProperty(name="Objects as OBJ Groups ", description="", default= False) + group_by_material = BoolProperty(name="Material Groups", description="", default= False) + keep_vertex_order = BoolProperty(name="Keep Vertex Order", description="", default= False) + + + def execute(self, context): + import io_scene_obj.export_obj + print(self.properties.keys()) + return io_scene_obj.export_obj.save(self, context, **self.properties) + + +def menu_func_import(self, context): + self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj)") + + +def menu_func_export(self, context): + self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)") + + +def register(): + bpy.types.INFO_MT_file_import.append(menu_func_import) + bpy.types.INFO_MT_file_export.append(menu_func_export) + +def unregister(): + bpy.types.INFO_MT_file_import.remove(menu_func_import) + bpy.types.INFO_MT_file_export.remove(menu_func_export) + + +# CONVERSION ISSUES +# - matrix problem +# - duplis - only tested dupliverts +# - all scenes export +# + normals calculation + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/io_scene_obj/export_obj.py b/release/scripts/op/io_scene_obj/export_obj.py index 2265177c85a..01f5b221546 100644 --- a/release/scripts/op/io_scene_obj/export_obj.py +++ b/release/scripts/op/io_scene_obj/export_obj.py @@ -734,7 +734,8 @@ def write_file(filepath, objects, scene, print("OBJ Export time: %.2f" % (time.clock() - time1)) -def write(filepath, context, +# +def _write(context, filepath, EXPORT_TRI, # ok EXPORT_EDGES, EXPORT_NORMALS, # not yet @@ -760,7 +761,7 @@ def write(filepath, context, orig_scene = context.scene # Exit edit mode before exporting, so current object states are exported properly. - if context.object: + if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') # if EXPORT_ALL_SCENES: @@ -831,98 +832,51 @@ def write(filepath, context, ''' Currently the exporter lacks these features: -* nurbs * multiple scene export (only active scene is written) * particles ''' -from bpy.props import * -from io_utils import ExportHelper - -class ExportOBJ(bpy.types.Operator, ExportHelper): - '''Save a Wavefront OBJ File''' - - bl_idname = "export.obj" - bl_label = 'Export OBJ' - - filename_ext = ".obj" - - # List of operator properties, the attributes will be assigned - # to the class instance from the operator settings before calling. - - # context group - use_selection = BoolProperty(name="Selection Only", description="Export selected objects only", default= False) - use_all_scenes = BoolProperty(name="All Scenes", description="", default= False) - use_animation = BoolProperty(name="Animation", description="", default= False) - - # object group - use_modifiers = BoolProperty(name="Apply Modifiers", description="Apply modifiers (preview resolution)", default= True) - use_rotate90 = BoolProperty(name="Rotate X90", description="", default= True) - - # extra data group - use_edges = BoolProperty(name="Edges", description="", default=True) - use_normals = BoolProperty(name="Normals", description="", default=False) - use_hq_normals = BoolProperty(name="High Quality Normals", description="", default=True) - use_uvs = BoolProperty(name="UVs", description="", default= True) - use_materials = BoolProperty(name="Materials", description="", default=True) - copy_images = BoolProperty(name="Copy Images", description="", default=False) - use_triangles = BoolProperty(name="Triangulate", description="", default=False) - use_vertex_groups = BoolProperty(name="Polygroups", description="", default=False) - use_nurbs = BoolProperty(name="Nurbs", description="", default=False) - - # grouping group - use_blen_objects = BoolProperty(name="Objects as OBJ Objects", description="", default= True) - group_by_object = BoolProperty(name="Objects as OBJ Groups ", description="", default= False) - group_by_material = BoolProperty(name="Material Groups", description="", default= False) - keep_vertex_order = BoolProperty(name="Keep Vertex Order", description="", default= False) - - - def execute(self, context): - - filepath = self.properties.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - - write(filepath, context, - EXPORT_TRI=self.properties.use_triangles, - EXPORT_EDGES=self.properties.use_edges, - EXPORT_NORMALS=self.properties.use_normals, - EXPORT_NORMALS_HQ=self.properties.use_hq_normals, - EXPORT_UV=self.properties.use_uvs, - EXPORT_MTL=self.properties.use_materials, - EXPORT_COPY_IMAGES=self.properties.copy_images, - EXPORT_APPLY_MODIFIERS=self.properties.use_modifiers, - EXPORT_ROTX90=self.properties.use_rotate90, - EXPORT_BLEN_OBS=self.properties.use_blen_objects, - EXPORT_GROUP_BY_OB=self.properties.group_by_object, - EXPORT_GROUP_BY_MAT=self.properties.group_by_material, - EXPORT_KEEP_VERT_ORDER=self.properties.keep_vertex_order, - EXPORT_POLYGROUPS=self.properties.use_vertex_groups, - EXPORT_CURVE_AS_NURBS=self.properties.use_nurbs, - EXPORT_SEL_ONLY=self.properties.use_selection, - EXPORT_ALL_SCENES=self.properties.use_all_scenes, - EXPORT_ANIMATION=self.properties.use_animation) - - return {'FINISHED'} - - -def menu_func(self, context): - self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)") - - -def register(): - bpy.types.INFO_MT_file_export.append(menu_func) - -def unregister(): - bpy.types.INFO_MT_file_export.remove(menu_func) - - -# CONVERSION ISSUES -# - matrix problem -# - duplis - only tested dupliverts -# - NURBS - needs API additions -# - all scenes export -# + normals calculation - -if __name__ == "__main__": - register() +def save(operator, context, filepath="", + use_triangles=False, + use_edges=False, + use_normals=False, + use_hq_normals=False, + use_uvs=True, + use_materials=True, + copy_images=False, + use_modifiers=True, + use_rotate_x90=True, + use_blen_objects=True, + group_by_object=False, + group_by_material=False, + keep_vertex_order=False, + use_vertex_groups=False, + use_nurbs=True, + use_selection=True, + use_all_scenes=False, + use_animation=False, + ): + + _write(context, filepath, + EXPORT_TRI=use_triangles, + EXPORT_EDGES=use_edges, + EXPORT_NORMALS=use_normals, + EXPORT_NORMALS_HQ=use_hq_normals, + EXPORT_UV=use_uvs, + EXPORT_MTL=use_materials, + EXPORT_COPY_IMAGES=copy_images, + EXPORT_APPLY_MODIFIERS=use_modifiers, + EXPORT_ROTX90=use_rotate_x90, + EXPORT_BLEN_OBS=use_blen_objects, + EXPORT_GROUP_BY_OB=group_by_object, + EXPORT_GROUP_BY_MAT=group_by_material, + EXPORT_KEEP_VERT_ORDER=keep_vertex_order, + EXPORT_POLYGROUPS=use_vertex_groups, + EXPORT_CURVE_AS_NURBS=use_nurbs, + EXPORT_SEL_ONLY=use_selection, + EXPORT_ALL_SCENES=use_all_scenes, + EXPORT_ANIMATION=use_animation, + ) + + return {'FINISHED'} diff --git a/release/scripts/op/io_scene_obj/import_obj.py b/release/scripts/op/io_scene_obj/import_obj.py index ab3c6ac19ca..441a11a5f6e 100644 --- a/release/scripts/op/io_scene_obj/import_obj.py +++ b/release/scripts/op/io_scene_obj/import_obj.py @@ -36,30 +36,8 @@ import time import bpy import mathutils from geometry import PolyFill +from io_utils import load_image, unpack_list, unpack_face_list -def unpack_list(list_of_tuples): - l = [] - for t in list_of_tuples: - l.extend(t) - return l - -# same as above except that it adds 0 for triangle faces -def unpack_face_list(list_of_tuples): - # allocate the entire list - flat_ls = [0] * (len(list_of_tuples) * 4) - i = 0 - - for t in list_of_tuples: - if len(t) == 3: - if t[2] == 0: - t = t[1], t[2], t[0] - else: # assuem quad - if t[3] == 0 or t[2] == 0: - t = t[2], t[3], t[0], t[1] - - flat_ls[i:i + len(t)] = t - i += 4 - return flat_ls def BPyMesh_ngon(from_data, indices, PREF_FIX_LOOPS= True): ''' @@ -260,21 +238,6 @@ def line_value(line_split): elif length > 2: return ' '.join( line_split[1:] ) -# limited replacement for BPyImage.comprehensiveImageLoad -def load_image(imagepath, dirname): - - if os.path.exists(imagepath): - return bpy.data.images.load(imagepath) - - variants = [imagepath, os.path.join(dirname, imagepath), os.path.join(dirname, os.path.basename(imagepath))] - - for filepath in variants: - for nfilepath in (filepath, bpy.path.resolve_ncase(filepath)): - if os.path.exists(nfilepath): - return bpy.data.images.load(nfilepath) - - # TODO comprehensiveImageLoad also searched in bpy.config.textureDir - return None def obj_image_load(imagepath, DIR, IMAGE_SEARCH): if '_' in imagepath: @@ -874,17 +837,16 @@ def get_float_func(filepath): # incase all vert values were ints return float -def load_obj(filepath, - context, - CLAMP_SIZE= 0.0, - CREATE_FGONS= True, - CREATE_SMOOTH_GROUPS= True, - CREATE_EDGES= True, - SPLIT_OBJECTS= True, - SPLIT_GROUPS= True, - ROTATE_X90= True, - IMAGE_SEARCH=True, - POLYGROUPS=False): +def load(operator, context, filepath, + CLAMP_SIZE= 0.0, + CREATE_FGONS= True, + CREATE_SMOOTH_GROUPS= True, + CREATE_EDGES= True, + SPLIT_OBJECTS= True, + SPLIT_GROUPS= True, + ROTATE_X90= True, + IMAGE_SEARCH=True, + POLYGROUPS=False): ''' Called by the user interface or another script. load_obj(path) - should give acceptable results. @@ -1218,295 +1180,8 @@ def load_obj(filepath, time_new= time.time() # time_new= sys.time() - print('%.4f sec' % (time_new-time_sub)) print('finished importing: %r in %.4f sec.' % (filepath, (time_new-time_main))) - - -DEBUG= True - - -def load_obj_ui(filepath, BATCH_LOAD= False): - if BPyMessages.Error_NoFile(filepath): - return - - global CREATE_SMOOTH_GROUPS, CREATE_FGONS, CREATE_EDGES, SPLIT_OBJECTS, SPLIT_GROUPS, CLAMP_SIZE, IMAGE_SEARCH, POLYGROUPS, KEEP_VERT_ORDER, ROTATE_X90 - - CREATE_SMOOTH_GROUPS= Draw.Create(0) - CREATE_FGONS= Draw.Create(1) - CREATE_EDGES= Draw.Create(1) - SPLIT_OBJECTS= Draw.Create(0) - SPLIT_GROUPS= Draw.Create(0) - CLAMP_SIZE= Draw.Create(10.0) - IMAGE_SEARCH= Draw.Create(1) - POLYGROUPS= Draw.Create(0) - KEEP_VERT_ORDER= Draw.Create(1) - ROTATE_X90= Draw.Create(1) - - - # Get USER Options - # Note, Works but not pretty, instead use a more complicated GUI - ''' - pup_block= [\ - 'Import...',\ - ('Smooth Groups', CREATE_SMOOTH_GROUPS, 'Surround smooth groups by sharp edges'),\ - ('Create FGons', CREATE_FGONS, 'Import faces with more then 4 verts as fgons.'),\ - ('Lines', CREATE_EDGES, 'Import lines and faces with 2 verts as edges'),\ - 'Separate objects from obj...',\ - ('Object', SPLIT_OBJECTS, 'Import OBJ Objects into Blender Objects'),\ - ('Group', SPLIT_GROUPS, 'Import OBJ Groups into Blender Objects'),\ - '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)'),\ - ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\ - ] - - if not Draw.PupBlock('Import OBJ...', pup_block): - return - - if KEEP_VERT_ORDER.val: - SPLIT_OBJECTS.val = False - SPLIT_GROUPS.val = False - ''' - - - - # BEGIN ALTERNATIVE UI ******************* - if True: - - EVENT_NONE = 0 - EVENT_EXIT = 1 - EVENT_REDRAW = 2 - EVENT_IMPORT = 3 - - GLOBALS = {} - GLOBALS['EVENT'] = EVENT_REDRAW - #GLOBALS['MOUSE'] = Window.GetMouseCoords() - GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] - - def obj_ui_set_event(e,v): - GLOBALS['EVENT'] = e - - def do_split(e,v): - global SPLIT_OBJECTS, SPLIT_GROUPS, KEEP_VERT_ORDER, POLYGROUPS - if SPLIT_OBJECTS.val or SPLIT_GROUPS.val: - KEEP_VERT_ORDER.val = 0 - POLYGROUPS.val = 0 - else: - KEEP_VERT_ORDER.val = 1 - - def do_vertorder(e,v): - global SPLIT_OBJECTS, SPLIT_GROUPS, KEEP_VERT_ORDER - if KEEP_VERT_ORDER.val: - SPLIT_OBJECTS.val = SPLIT_GROUPS.val = 0 - else: - if not (SPLIT_OBJECTS.val or SPLIT_GROUPS.val): - KEEP_VERT_ORDER.val = 1 - - def do_polygroups(e,v): - global SPLIT_OBJECTS, SPLIT_GROUPS, KEEP_VERT_ORDER, POLYGROUPS - if POLYGROUPS.val: - SPLIT_OBJECTS.val = SPLIT_GROUPS.val = 0 - - def do_help(e,v): - url = __url__[0] - print('Trying to open web browser with documentation at this address...') - print('\t' + url) - - try: - import webbrowser - webbrowser.open(url) - except: - print('...could not open a browser window.') - - def obj_ui(): - ui_x, ui_y = GLOBALS['MOUSE'] - - # Center based on overall pup size - ui_x -= 165 - ui_y -= 90 - - global CREATE_SMOOTH_GROUPS, CREATE_FGONS, CREATE_EDGES, SPLIT_OBJECTS, SPLIT_GROUPS, CLAMP_SIZE, IMAGE_SEARCH, POLYGROUPS, KEEP_VERT_ORDER, ROTATE_X90 - - Draw.Label('Import...', ui_x+9, ui_y+159, 220, 21) - Draw.BeginAlign() - CREATE_SMOOTH_GROUPS = Draw.Toggle('Smooth Groups', EVENT_NONE, ui_x+9, ui_y+139, 110, 20, CREATE_SMOOTH_GROUPS.val, 'Surround smooth groups by sharp edges') - CREATE_FGONS = Draw.Toggle('NGons as FGons', EVENT_NONE, ui_x+119, ui_y+139, 110, 20, CREATE_FGONS.val, 'Import faces with more then 4 verts as fgons') - CREATE_EDGES = Draw.Toggle('Lines as Edges', EVENT_NONE, ui_x+229, ui_y+139, 110, 20, CREATE_EDGES.val, 'Import lines and faces with 2 verts as edges') - Draw.EndAlign() - - Draw.Label('Separate objects by OBJ...', ui_x+9, ui_y+110, 220, 20) - 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) - Draw.EndAlign() - - # Only used for user feedback - KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+184, ui_y+89, 113, 21, KEEP_VERT_ORDER.val, 'Keep vert and face order, disables split options, enable for morph targets', do_vertorder) - - ROTATE_X90 = Draw.Toggle('-X90', EVENT_REDRAW, ui_x+302, ui_y+89, 38, 21, ROTATE_X90.val, 'Rotate X 90.') - - Draw.Label('Options...', ui_x+9, ui_y+60, 211, 20) - CLAMP_SIZE = Draw.Number('Clamp Scale: ', EVENT_NONE, ui_x+9, ui_y+39, 130, 21, CLAMP_SIZE.val, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)') - POLYGROUPS = Draw.Toggle('Poly Groups', EVENT_REDRAW, ui_x+144, ui_y+39, 90, 21, POLYGROUPS.val, 'Import OBJ groups as vertex groups.', do_polygroups) - IMAGE_SEARCH = Draw.Toggle('Image Search', EVENT_NONE, ui_x+239, ui_y+39, 100, 21, IMAGE_SEARCH.val, 'Search subdirs for any assosiated images (Warning, may be slow)') - Draw.BeginAlign() - Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 21, 'Load the wiki page for this script', do_help) - Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 21, '', obj_ui_set_event) - Draw.PushButton('Import', EVENT_IMPORT, ui_x+229, ui_y+9, 110, 21, 'Import with these settings', obj_ui_set_event) - Draw.EndAlign() - - - # hack so the toggle buttons redraw. this is not nice at all - while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_IMPORT): - Draw.UIBlock(obj_ui, 0) - - if GLOBALS['EVENT'] != EVENT_IMPORT: - return - - # END ALTERNATIVE UI ********************* - - - - - - - - Window.WaitCursor(1) - - if BATCH_LOAD: # load the dir - try: - files= [ f for f in os.listdir(filepath) if f.lower().endswith('.obj') ] - except: - Window.WaitCursor(0) - Draw.PupMenu('Error%t|Could not open path ' + filepath) - return - - if not files: - Window.WaitCursor(0) - Draw.PupMenu('Error%t|No files at path ' + filepath) - return - - for f in files: - scn= bpy.data.scenes.new(os.path.splitext(f)[0]) - scn.makeCurrent() - - load_obj(sys.join(filepath, f),\ - CLAMP_SIZE.val,\ - CREATE_FGONS.val,\ - CREATE_SMOOTH_GROUPS.val,\ - CREATE_EDGES.val,\ - SPLIT_OBJECTS.val,\ - SPLIT_GROUPS.val,\ - ROTATE_X90.val,\ - IMAGE_SEARCH.val,\ - POLYGROUPS.val - ) - - else: # Normal load - load_obj(filepath,\ - CLAMP_SIZE.val,\ - CREATE_FGONS.val,\ - CREATE_SMOOTH_GROUPS.val,\ - CREATE_EDGES.val,\ - SPLIT_OBJECTS.val,\ - SPLIT_GROUPS.val,\ - ROTATE_X90.val,\ - IMAGE_SEARCH.val,\ - POLYGROUPS.val - ) - - Window.WaitCursor(0) - - -def load_obj_ui_batch(file): - load_obj_ui(file, True) - -DEBUG= False - -# if __name__=='__main__' and not DEBUG: -# if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT: -# Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '') -# else: -# Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj') - - # For testing compatibility -''' -else: - # DEBUG ONLY - TIME= sys.time() - DIR = '/fe/obj' - import os - print 'Searching for files' - def fileList(path): - for dirpath, dirnames, filenames in os.walk(path): - for filename in filenames: - yield os.path.join(dirpath, filename) - - files = [f for f in fileList(DIR) if f.lower().endswith('.obj')] - files.sort() - - for i, obj_file in enumerate(files): - if 0 < i < 20: - print 'Importing', obj_file, '\nNUMBER', i, 'of', len(files) - newScn= bpy.data.scenes.new(os.path.basename(obj_file)) - newScn.makeCurrent() - load_obj(obj_file, False, IMAGE_SEARCH=0) - - print 'TOTAL TIME: %.6f' % (sys.time() - TIME) -''' - -from bpy.props import * -from io_utils import ImportHelper - - -class IMPORT_OT_obj(bpy.types.Operator, ImportHelper): - '''Load a Wavefront OBJ File''' - bl_idname = "import_scene.obj" - bl_label = "Import OBJ" - - filename_ext = ".obj" - - 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) - # 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) - ROTATE_X90 = BoolProperty(name="-X90", description="Rotate X 90.", default= True) - CLAMP_SIZE = FloatProperty(name="Clamp Scale", description="Clamp the size to this maximum (Zero to Disable)", min=0.0, max=1000.0, soft_min=0.0, soft_max=1000.0, default=0.0) - POLYGROUPS = BoolProperty(name="Poly Groups", description="Import OBJ groups as vertex groups.", default= True) - IMAGE_SEARCH = BoolProperty(name="Image Search", description="Search subdirs for any assosiated images (Warning, may be slow)", default= True) - - - def execute(self, context): - # print("Selected: " + context.active_object.name) - - load_obj(self.properties.filepath, - context, - self.properties.CLAMP_SIZE, - self.properties.CREATE_FGONS, - self.properties.CREATE_SMOOTH_GROUPS, - self.properties.CREATE_EDGES, - self.properties.SPLIT_OBJECTS, - self.properties.SPLIT_GROUPS, - self.properties.ROTATE_X90, - self.properties.IMAGE_SEARCH, - self.properties.POLYGROUPS) - - return {'FINISHED'} - - -def menu_func(self, context): - self.layout.operator(IMPORT_OT_obj.bl_idname, text="Wavefront (.obj)") - - -def register(): - bpy.types.INFO_MT_file_import.append(menu_func) - -def unregister(): - bpy.types.INFO_MT_file_import.remove(menu_func) + return {'FINISHED'} # NOTES (all line numbers refer to 2.4x import_obj.py, not this file) diff --git a/release/scripts/op/io_scene_x3d/__init__.py b/release/scripts/op/io_scene_x3d/__init__.py new file mode 100644 index 00000000000..4ccd29808bf --- /dev/null +++ b/release/scripts/op/io_scene_x3d/__init__.py @@ -0,0 +1,61 @@ +# ##### 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 ##### + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + # only reload if we alredy loaded, highly annoying + import sys + reload(sys.modules.get("io_scene_x3d.export_x3d", sys)) + + +import bpy +from bpy.props import * +from io_utils import ExportHelper + + +class ExportX3D(bpy.types.Operator, ExportHelper): + '''Export selection to Extensible 3D file (.x3d)''' + bl_idname = "export_scene.x3d" + bl_label = 'Export X3D' + + filename_ext = ".x3d" + + use_apply_modifiers = BoolProperty(name="Apply Modifiers", description="Use transformed mesh data from each object", default=True) + use_triangulate = BoolProperty(name="Triangulate", description="Triangulate quads.", default=False) + use_compress = BoolProperty(name="Compress", description="GZip the resulting file, requires a full python install", default=False) + + def execute(self, context): + import io_scene_x3d.export_x3d + return io_scene_x3d.export_x3d.save(self, context, **self.properties) + + +def menu_func(self, context): + self.layout.operator(ExportX3D.bl_idname, text="X3D Extensible 3D (.x3d)") + + +def register(): + bpy.types.INFO_MT_file_export.append(menu_func) + +def unregister(): + bpy.types.INFO_MT_file_export.remove(menu_func) + +# NOTES +# - blender version is hardcoded + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/io_scene_x3d/export_x3d.py b/release/scripts/op/io_scene_x3d/export_x3d.py index 9dc9c6cb981..dd8b9b2c5f6 100644 --- a/release/scripts/op/io_scene_x3d/export_x3d.py +++ b/release/scripts/op/io_scene_x3d/export_x3d.py @@ -48,8 +48,7 @@ MATWORLD= mathutils.Matrix.Rotation(-90, 4, 'X') # Global Variables #################################### -filename = "" -# filename = Blender.Get('filename') +filepath = "" _safeOverwrite = True extension = '' @@ -60,7 +59,7 @@ extension = '' class x3d_class: - def __init__(self, filename): + def __init__(self, filepath): #--- public you can change these --- self.writingcolor = 0 self.writingtexture = 0 @@ -83,18 +82,18 @@ class x3d_class: self.matNames={} # dictionary of materiaNames self.meshNames={} # dictionary of meshNames self.indentLevel=0 # keeps track of current indenting - self.filename=filename + self.filepath=filepath self.file = None - if filename.lower().endswith('.x3dz'): + if filepath.lower().endswith('.x3dz'): try: import gzip - self.file = gzip.open(filename, "w") + self.file = gzip.open(filepath, "w") except: print("failed to import compression modules, exporting uncompressed") - self.filename = filename[:-1] # remove trailing z + self.filepath = filepath[:-1] # remove trailing z if self.file == None: - self.file = open(self.filename, "w") + self.file = open(self.filepath, "w") self.bNav=0 self.nodeID=0 @@ -136,8 +135,8 @@ class x3d_class: ########################################################## def writeHeader(self): - #bfile = sys.expandpath( Blender.Get('filename') ).replace('<', '<').replace('>', '>') - bfile = self.filename.replace('<', '<').replace('>', '>') # use outfile name + #bfile = sys.expandpath( Blender.Get('filepath') ).replace('<', '<').replace('>', '>') + bfile = self.filepath.replace('<', '<').replace('>', '>') # use outfile name self.file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") self.file.write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.0//EN\" \"http://www.web3d.org/specifications/x3d-3.0.dtd\">\n") self.file.write("<X3D version=\"3.0\" profile=\"Immersive\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema-instance\" xsd:noNamespaceSchemaLocation=\"http://www.web3d.org/specifications/x3d-3.0.xsd\">\n") @@ -426,6 +425,7 @@ class x3d_class: self.writeIndented("<Appearance>\n", 1) # right now this script can only handle a single material per mesh. if len(maters) >= 1 and maters[0].use_face_texture == False: + mat = maters[0] self.writeMaterial(mat, self.cleanStr(mat.name,''), world) if len(maters) > 1: print("Warning: mesh named %s has multiple materials" % meshName) @@ -672,13 +672,13 @@ class x3d_class: def writeImageTexture(self, image): name = image.name - filename = os.path.basename(image.filepath) + filepath = os.path.basename(image.filepath) if name in self.texNames: self.writeIndented("<ImageTexture USE=\"%s\" />\n" % self.cleanStr(name)) self.texNames[name] += 1 else: self.writeIndented("<ImageTexture DEF=\"%s\" " % self.cleanStr(name), 1) - self.file.write("url=\"%s\" />" % filename) + self.file.write("url=\"%s\" />" % filepath) self.writeIndented("\n",-1) self.texNames[name] = 1 @@ -781,7 +781,7 @@ class x3d_class: EXPORT_TRI= False,\ ): - print("Info: starting X3D export to " + self.filename + "...") + print("Info: starting X3D export to " + self.filepath + "...") self.writeHeader() # self.writeScript() self.writeNavigationInfo(scene) @@ -879,7 +879,7 @@ class x3d_class: self.texNames={} self.matNames={} self.indentLevel=0 - print("Info: finished X3D export to %s\n" % self.filename) + print("Info: finished X3D export to %s\n" % self.filepath) def cleanStr(self, name, prefix='rsvd_'): """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name""" @@ -1089,82 +1089,35 @@ class x3d_class: # Callbacks, needed before Main ########################################################## -def write(filename, - context, - EXPORT_APPLY_MODIFIERS=False, - EXPORT_TRI=False, - EXPORT_GZIP=False): +def save(operator, context, filepath="", + use_apply_modifiers=False, + use_triangulate=False, + use_compress=False): - if EXPORT_GZIP: - if not filename.lower().endswith('.x3dz'): - filename = '.'.join(filename.split('.')[:-1]) + '.x3dz' + if use_compress: + if not filepath.lower().endswith('.x3dz'): + filepath = '.'.join(filepath.split('.')[:-1]) + '.x3dz' else: - if not filename.lower().endswith('.x3d'): - filename = '.'.join(filename.split('.')[:-1]) + '.x3d' - + if not filepath.lower().endswith('.x3d'): + filepath = '.'.join(filepath.split('.')[:-1]) + '.x3d' scene = context.scene world = scene.world - if scene.objects.active: + if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') # XXX these are global textures while .Get() returned only scene's? alltextures = bpy.data.textures # alltextures = Blender.Texture.Get() - wrlexport=x3d_class(filename) - wrlexport.export(\ - scene,\ - world,\ - alltextures,\ - \ - EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS,\ - EXPORT_TRI = EXPORT_TRI,\ - ) - - -from bpy.props import * -from io_utils import ExportHelper - - -class ExportX3D(bpy.types.Operator, ExportHelper): - '''Export selection to Extensible 3D file (.x3d)''' - bl_idname = "export.x3d" - bl_label = 'Export X3D' - - filename_ext = ".x3d" - - apply_modifiers = BoolProperty(name="Apply Modifiers", description="Use transformed mesh data from each object", default=True) - triangulate = BoolProperty(name="Triangulate", description="Triangulate quads.", default=False) - compress = BoolProperty(name="Compress", description="GZip the resulting file, requires a full python install", default=False) - - def execute(self, context): - filepath = self.properties.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - - write(filepath, - context, - self.properties.apply_modifiers, - self.properties.triangulate, - self.properties.compress, - ) - - return {'FINISHED'} - - -def menu_func(self, context): - self.layout.operator(ExportX3D.bl_idname, text="X3D Extensible 3D (.x3d)") - - -def register(): - bpy.types.INFO_MT_file_export.append(menu_func) - -def unregister(): - bpy.types.INFO_MT_file_export.remove(menu_func) + wrlexport = x3d_class(filepath) + wrlexport.export(scene, + world, + alltextures, + EXPORT_APPLY_MODIFIERS=use_apply_modifiers, + EXPORT_TRI=use_triangulate, + ) -# NOTES -# - blender version is hardcoded + return {'FINISHED'} -if __name__ == "__main__": - register() diff --git a/release/scripts/op/io_shape_mdd/__init__.py b/release/scripts/op/io_shape_mdd/__init__.py new file mode 100644 index 00000000000..0af4af92b7c --- /dev/null +++ b/release/scripts/op/io_shape_mdd/__init__.py @@ -0,0 +1,115 @@ +# ##### 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> + +# To support reload properly, try to access a package var, if it's there, reload everything +if "bpy" in locals(): + # only reload if we alredy loaded, highly annoying + import sys + reload(sys.modules.get("io_shape_mdd.import_mdd", sys)) + reload(sys.modules.get("io_shape_mdd.export_mdd", sys)) + + +import bpy +from bpy.props import * +from io_utils import ExportHelper, ImportHelper + + +class ImportMDD(bpy.types.Operator, ImportHelper): + '''Import MDD vertex keyframe file to shape keys''' + bl_idname = "import_shape.mdd" + bl_label = "Import MDD" + + filename_ext = ".mdd" + frame_start = IntProperty(name="Start Frame", description="Start frame for inserting animation", min=-300000, max=300000, default=0) + frame_step = IntProperty(name="Step", min=1, max=1000, default=1) + + @classmethod + def poll(cls, context): + ob = context.active_object + return (ob and ob.type == 'MESH') + + def execute(self, context): + + # initialize from scene if unset + scene = context.scene + if not self.properties.is_property_set("frame_start"): + self.properties.frame_start = scene.frame_current + + import io_shape_mdd.import_mdd + return io_shape_mdd.import_mdd.load(self, context, **self.properties) + +class ExportMDD(bpy.types.Operator, ExportHelper): + '''Animated mesh to MDD vertex keyframe file''' + bl_idname = "export_shape.mdd" + bl_label = "Export MDD" + + filename_ext = ".mdd" + + # get first scene to get min and max properties for frames, fps + + minframe = 1 + maxframe = 300000 + minfps = 1 + maxfps = 120 + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + fps = IntProperty(name="Frames Per Second", description="Number of frames/second", min=minfps, max=maxfps, default=25) + 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) + + @classmethod + def poll(cls, context): + obj = context.active_object + return (obj and obj.type == 'MESH') + + def execute(self, context): + # initialize from scene if unset + scene = context.scene + if not self.properties.is_property_set("frame_start"): + self.properties.frame_start = scene.frame_start + if not self.properties.is_property_set("frame_end"): + self.properties.frame_end = scene.frame_end + if not self.properties.is_property_set("fps"): + self.properties.fps = scene.render.fps + + import io_shape_mdd.export_mdd + return io_shape_mdd.export_mdd.save(self, context, **self.properties) + + +def menu_func_import(self, context): + self.layout.operator(ImportMDD.bl_idname, text="Lightwave Point Cache (.mdd)") + + +def menu_func_export(self, context): + self.layout.operator(ExportMDD.bl_idname, text="Lightwave Point Cache (.mdd)") + + +def register(): + bpy.types.INFO_MT_file_import.append(menu_func_import) + bpy.types.INFO_MT_file_export.append(menu_func_export) + + +def unregister(): + bpy.types.INFO_MT_file_import.remove(menu_func_import) + bpy.types.INFO_MT_file_export.remove(menu_func_export) + +if __name__ == "__main__": + register() diff --git a/release/scripts/op/io_shape_mdd/export_mdd.py b/release/scripts/op/io_shape_mdd/export_mdd.py index 91d75aaf445..1f0a5d94320 100644 --- a/release/scripts/op/io_shape_mdd/export_mdd.py +++ b/release/scripts/op/io_shape_mdd/export_mdd.py @@ -54,19 +54,23 @@ def check_vertcount(mesh, vertcount): return -def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): +def save(operator, context, filepath="", frame_start=1, frame_end=300, fps=25): """ Blender.Window.WaitCursor(1) mesh_orig = Mesh.New() - mesh_orig.getFromObject(ob.name) + mesh_orig.getFromObject(obj.name) """ - bpy.ops.object.mode_set(mode='OBJECT') + scene = context.scene + obj = context.object - orig_frame = sce.frame_current - sce.set_frame(PREF_STARTFRAME) - me = ob.create_mesh(sce, True, 'PREVIEW') + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') + + orig_frame = scene.frame_current + scene.set_frame(frame_start) + me = obj.create_mesh(scene, True, 'PREVIEW') #Flip y and z mat_flip = mathutils.Matrix(\ @@ -78,36 +82,36 @@ def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): numverts = len(me.vertices) - numframes = PREF_ENDFRAME - PREF_STARTFRAME + 1 - PREF_FPS = float(PREF_FPS) - f = open(filename, 'wb') #no Errors yet:Safe to create file + numframes = frame_end - frame_start + 1 + fps = float(fps) + f = open(filepath, 'wb') #no Errors yet:Safe to create file # Write the header f.write(pack(">2i", numframes, numverts)) # Write the frame times (should we use the time IPO??) - f.write(pack(">%df" % (numframes), *[frame / PREF_FPS for frame in range(numframes)])) # seconds + f.write(pack(">%df" % (numframes), *[frame / fps for frame in range(numframes)])) # seconds #rest frame needed to keep frames in sync """ - Blender.Set('curframe', PREF_STARTFRAME) - me_tmp.getFromObject(ob.name) + Blender.Set('curframe', frame_start) + me_tmp.getFromObject(obj.name) """ check_vertcount(me, numverts) - me.transform(mat_flip * ob.matrix_world) + me.transform(mat_flip * obj.matrix_world) f.write(pack(">%df" % (numverts * 3), *[axis for v in me.vertices for axis in v.co])) - for frame in range(PREF_STARTFRAME, PREF_ENDFRAME + 1):#in order to start at desired frame + for frame in range(frame_start, frame_end + 1):#in order to start at desired frame """ Blender.Set('curframe', frame) - me_tmp.getFromObject(ob.name) + me_tmp.getFromObject(obj.name) """ - sce.set_frame(frame) - me = ob.create_mesh(sce, True, 'PREVIEW') + scene.set_frame(frame) + me = obj.create_mesh(scene, True, 'PREVIEW') check_vertcount(me, numverts) - me.transform(mat_flip * ob.matrix_world) + me.transform(mat_flip * obj.matrix_world) # Write the vertex data f.write(pack(">%df" % (numverts * 3), *[axis for v in me.vertices for axis in v.co])) @@ -117,67 +121,11 @@ def write(filename, sce, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): """ f.close() - print('MDD Exported: %s frames:%d\n' % (filename, numframes - 1)) + print('MDD Exported: %r frames:%d\n' % (filepath, numframes - 1)) """ Blender.Window.WaitCursor(0) Blender.Set('curframe', orig_frame) """ - sce.set_frame(orig_frame) - -from bpy.props import * -from io_utils import ExportHelper - - -class ExportMDD(bpy.types.Operator, ExportHelper): - '''Animated mesh to MDD vertex keyframe file''' - bl_idname = "export.mdd" - bl_label = "Export MDD" + scene.set_frame(orig_frame) - filename_ext = ".mdd" - - # get first scene to get min and max properties for frames, fps - - minframe = 1 - maxframe = 300000 - minfps = 1 - maxfps = 120 - - # List of operator properties, the attributes will be assigned - # to the class instance from the operator settings before calling. - fps = IntProperty(name="Frames Per Second", description="Number of frames/second", min=minfps, max=maxfps, default=25) - 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) - - @classmethod - def poll(cls, context): - ob = context.active_object - return (ob and ob.type == 'MESH') - - def execute(self, context): - filepath = self.properties.filepath - filepath = bpy.path.ensure_ext(filepath, self.filename_ext) - - write(filepath, - context.scene, - context.active_object, - self.properties.frame_start, - self.properties.frame_end, - self.properties.fps, - ) - - return {'FINISHED'} - - -def menu_func(self, context): - self.layout.operator(ExportMDD.bl_idname, text="Lightwave Point Cache (.mdd)") - - -def register(): - bpy.types.INFO_MT_file_export.append(menu_func) - - -def unregister(): - bpy.types.INFO_MT_file_export.remove(menu_func) - -if __name__ == "__main__": - register() + return {'FINISHED'} diff --git a/release/scripts/op/io_shape_mdd/import_mdd.py b/release/scripts/op/io_shape_mdd/import_mdd.py index ff67d3bf7f3..d008ff931ff 100644 --- a/release/scripts/op/io_shape_mdd/import_mdd.py +++ b/release/scripts/op/io_shape_mdd/import_mdd.py @@ -35,11 +35,15 @@ import bpy from struct import unpack -def mdd_import(filepath, ob, scene, PREF_START_FRAME=0, PREF_JUMP=1): +def load(operator, context, filepath, frame_start=0, frame_step=1): + + scene = context.scene + obj = context.object + + print('\n\nimporting mdd %r' % filepath) - print('\n\nimporting mdd "%s"' % filepath) - - bpy.ops.object.mode_set(mode='OBJECT') + if bpy.ops.object.mode_set.poll(): + bpy.ops.object.mode_set(mode='OBJECT') file = open(filepath, 'rb') frames, points = unpack(">2i", file.read(8)) @@ -49,92 +53,53 @@ def mdd_import(filepath, ob, scene, PREF_START_FRAME=0, PREF_JUMP=1): # If target object doesn't have Basis shape key, create it. try: - num_keys = len(ob.data.shape_keys.keys) + num_keys = len(obj.data.shape_keys.keys) except: - basis = ob.add_shape_key() + basis = obj.add_shape_key() basis.name = "Basis" - ob.data.update() + obj.data.update() - scene.frame_current = PREF_START_FRAME + scene.frame_current = frame_start def UpdateMesh(ob, fr): # Insert new shape key - new_shapekey = ob.add_shape_key() + new_shapekey = obj.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.show_shape_key = True + obj.active_shape_key_index = len(obj.data.shape_keys.keys)-1 + index = len(obj.data.shape_keys.keys)-1 + obj.show_shape_key = True - verts = ob.data.shape_keys.keys[len(ob.data.shape_keys.keys)-1].data + verts = obj.data.shape_keys.keys[len(obj.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.show_shape_key = False + obj.show_shape_key = False # insert keyframes - shape_keys = ob.data.shape_keys + shape_keys = obj.data.shape_keys 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") + obj.data.shape_keys.keys[index].value = 0.0 + shape_keys.keys[len(obj.data.shape_keys.keys)-1].keyframe_insert("value") 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") + obj.data.shape_keys.keys[index].value = 1.0 + shape_keys.keys[len(obj.data.shape_keys.keys)-1].keyframe_insert("value") 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") + obj.data.shape_keys.keys[index].value = 0.0 + shape_keys.keys[len(obj.data.shape_keys.keys)-1].keyframe_insert("value") - ob.data.update() + obj.data.update() for i in range(frames): - UpdateMesh(ob, i) - - -from bpy.props import * -from io_utils import ImportHelper - - -class importMDD(bpy.types.Operator, ImportHelper): - '''Import MDD vertex keyframe file to shape keys''' - bl_idname = "import_shape.mdd" - bl_label = "Import MDD" - - filename_ext = ".mdd" - frame_start = IntProperty(name="Start Frame", description="Start frame for inserting animation", min=-300000, max=300000, default=0) - - @classmethod - def poll(cls, context): - ob = context.active_object - return (ob and ob.type == 'MESH') - - def execute(self, context): - if not self.properties.filepath: - raise Exception("filename not set") - - mdd_import(self.properties.filepath, bpy.context.active_object, context.scene, self.properties.frame_start, 1) - - return {'FINISHED'} - - -def menu_func(self, context): - self.layout.operator(importMDD.bl_idname, text="Lightwave Point Cache (.mdd)") - - -def register(): - bpy.types.INFO_MT_file_import.append(menu_func) - - -def unregister(): - bpy.types.INFO_MT_file_import.remove(menu_func) + UpdateMesh(obj, i) -if __name__ == "__main__": - register() + return {'FINISHED'} |