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
This commit is contained in:
2010-09-01 12:11:34 +00:00
parent 7532bc2325
commit a89c526a92
19 changed files with 1044 additions and 1241 deletions

View File

@@ -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():

View File

@@ -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):

View File

@@ -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()

View File

@@ -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"
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)
print('Done in %.4f\n' % (time.time() - t1))
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'}
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()
return {'FINISHED'}

View File

@@ -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()

View File

@@ -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):
def rvec2d(v):
return round(v[0], 6), round(v[1], 6)
scene = context.scene
obj = context.object
if not filename.lower().endswith('.ply'):
filename += '.ply'
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'}

View File

@@ -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()

View File

@@ -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'}

View File

@@ -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_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 = 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_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 = int ('0x0010', 16) #color defined as 3 floats
MAT_24BIT_COLOR = int ('0x0011', 16) #color defined as 3 bytes
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_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= int('0x4700',16); # This lets un know we are reading a camera 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
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'}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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
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,
):
class ExportOBJ(bpy.types.Operator, ExportHelper):
'''Save a Wavefront OBJ File'''
_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,
)
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()
return {'FINISHED'}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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('<', '&lt').replace('>', '&gt')
bfile = self.filename.replace('<', '&lt').replace('>', '&gt') # use outfile name
#bfile = sys.expandpath( Blender.Get('filepath') ).replace('<', '&lt').replace('>', '&gt')
bfile = self.filepath.replace('<', '&lt').replace('>', '&gt') # 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,\
)
wrlexport = x3d_class(filepath)
wrlexport.export(scene,
world,
alltextures,
EXPORT_APPLY_MODIFIERS=use_apply_modifiers,
EXPORT_TRI=use_triangulate,
)
return {'FINISHED'}
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)
# NOTES
# - blender version is hardcoded
if __name__ == "__main__":
register()

View File

@@ -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()

View File

@@ -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'}

View File

@@ -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)
UpdateMesh(obj, 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)
if __name__ == "__main__":
register()
return {'FINISHED'}