diff options
author | Campbell Barton <ideasman42@gmail.com> | 2006-01-11 12:47:17 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2006-01-11 12:47:17 +0300 |
commit | 664f6026d57b6854b7865ee2d9c1129aff0beecd (patch) | |
tree | ae88670c48e19fb55ee9466fe7340a6f264d28f6 /release | |
parent | 85e8dd5c059bc5e6f2eb81e39530d90c2438b6cd (diff) |
update to obj export:
Added options.
pup_block = [\
('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\
('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection.'),\
('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\
('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\
('Materials', EXPORT_MTL, 'Write a seperate MTL file with the OBJ.'),\
('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a seperate OBJ file.'),\
('Animation', EXPORT_ANIMATION, 'Each frame as a seperate OBJ file.'),\
('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never everwrite.'),\
]
Obj import was updated to import edges (2 vert faces), will update some more later.
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/obj_export.py | 393 | ||||
-rw-r--r-- | release/scripts/obj_import.py | 16 |
2 files changed, 330 insertions, 79 deletions
diff --git a/release/scripts/obj_export.py b/release/scripts/obj_export.py index c220d66b709..d0a993509f9 100644 --- a/release/scripts/obj_export.py +++ b/release/scripts/obj_export.py @@ -42,12 +42,24 @@ Run this script from "File->Export" menu to export all meshes. # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- + +import Blender +from Blender import Mesh, Scene, Window, sys, Image, Draw + #==================================================# # New name based on old with a different extension # #==================================================# def newFName(ext): - return Get('filename')[: -len(Get('filename').split('.', -1)[-1]) ] + ext + return Blender.Get('filename')[: -len(Blender.Get('filename').split('.', -1)[-1]) ] + ext +# Returns a tuple - path,extension. +# 'hello.obj' > ('hello', '.obj') +def splitExt(path): + dotidx = path.rfind('.') + if dotidx == -1: + return path, '' + else: + return path[:dotidx], path[dotidx:] def fixName(name): if name == None: @@ -55,10 +67,53 @@ def fixName(name): else: return name.replace(' ', '_') +# Used to add the scene name into the filename without using odd chars +def saneFilechars(name): + for ch in ' /\\~!@#$%^&*()+=[];\':",./<>?': + name = name.replace(ch, '_') + return name +def sortPair(a,b): + return min(a,b), max(a,b) - -from Blender import * +def getMeshFromObject(object, name=None, mesh=None): + if mesh: + mesh.verts = None # Clear the meshg + else: + if not name: + mesh = Mesh.New() + else: + mesh = Mesh.New(name) + + + type = object.getType() + dataname = object.getData(1) + + try: + mesh.getFromObject(object.name) + except: + return None + + if type == 'Mesh': + tempMe = Mesh.Get( dataname ) + mesh.materials = tempMe.materials + mesh.degr = tempMe.degr + mesh.mode = tempMe.mode + else: + try: + # Will only work for curves!! + # Text- no material access in python interface. + # Surf- no python interface + # MBall- no material access in python interface. + + data = object.getData() + materials = data.getMaterials() + mesh.materials = materials + print 'assigning materials for non mesh' + except: + print 'Cant assign materials to', type + + return mesh global MTL_DICT @@ -69,14 +124,14 @@ MTL_DICT = {} def save_mtl(filename): global MTL_DICT - world = World.GetCurrent() + world = Blender.World.GetCurrent() if world: worldAmb = world.getAmb() else: worldAmb = (0,0,0) # Default value file = open(filename, "w") - file.write('# Blender MTL File: %s\n' % Get('filename').split('\\')[-1].split('/')[-1]) + file.write('# Blender MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) file.write('# Material Count: %i\n' % len(MTL_DICT)) # Write material/image combinations we have used. for key, mtl_mat_name in MTL_DICT.iteritems(): @@ -96,7 +151,7 @@ def save_mtl(filename): file.write('illum 2\n') # light normaly else: - mat = Material.Get(key[0]) + mat = Blender.Material.Get(key[0]) file.write('Ns %s\n' % round((mat.getHardness()-1) * 1.9607843137254901 ) ) # Hardness, convert blenders 1-511 to MTL's file.write('Ka %s %s %s\n' % tuple([round(c*mat.getAmb(), 6) for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd %s %s %s\n' % tuple([round(c*mat.getRef(), 6) for c in mat.getRGBCol()]) ) # Diffuse @@ -105,7 +160,7 @@ def save_mtl(filename): file.write('d %s\n' % round(mat.getAlpha(), 6)) # Alpha (obj uses 'd' for dissolve) # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. - if mat.getMode() & Material.Modes['SHADELESS']: + if mat.getMode() & Blender.Material.Modes['SHADELESS']: file.write('illum 0\n') # ignore lighting elif mat.getSpec() == 0: file.write('illum 1\n') # no specular. @@ -120,7 +175,7 @@ def save_mtl(filename): elif key[0] != None: # No face image. if we havea material search for MTex image. for mtex in mat.getTextures(): - if mtex and mtex.tex.type == Texture.Types.IMAGE: + if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: try: filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1] file.write('map_Kd %s\n' % filename) # Diffuse mapping image @@ -133,24 +188,77 @@ def save_mtl(filename): file.close() +def copy_file(source, dest): + file = open(source, 'rb') + data = file.read() + file.close() + + file = open(dest, 'wb') + file.write(data) + file.close() - -def save_obj(filename): - global MTL_DICT +def copy_images(dest_dir): + if dest_dir[-1] != sys.sep: + dest_dir += sys.sep + + # Get unique image names + uniqueImages = {} + for matname, imagename in MTL_DICT.iterkeys(): # Only use image name + uniqueImages[imagename] = None # Should use sets here. wait until Python 2.4 is default. + + # Now copy images + copyCount = 0 + for imageName in uniqueImages.iterkeys(): + bImage = Image.Get(imageName) + image_path = sys.expandpath(bImage.filename) + if sys.exists(image_path): + # Make a name for the target path. + dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] + if not sys.exists(dest_image_path): # Image isnt alredy there + print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) + copy_file(image_path, dest_image_path) + copyCount+=1 + print '\tCopied %d images' % copyCount + +def save_obj(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True): + ''' + Basic save function. The context and options must be alredy set + This can be accessed externaly + eg. + save_obj( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. + ''' + print 'OBJ Export path: "%s"' % filename + global MTL_DICT + temp_mesh_name = '~tmp-mesh' time1 = sys.time() scn = Scene.GetCurrent() file = open(filename, "w") # Write Header - file.write('# Blender OBJ File: %s\n' % (Get('filename').split('/')[-1].split('\\')[-1] )) + file.write('# Blender OBJ File: %s\n' % (Blender.Get('filename').split('/')[-1].split('\\')[-1] )) file.write('# www.blender.org\n') # Tell the obj file what material file to use. mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) - + + # Get the container mesh. + if EXPORT_APPLY_MODIFIERS: + containerMesh = meshName = tempMesh = None + for meshName in Blender.NMesh.GetNames(): + if meshName.startswith(temp_mesh_name): + tempMesh = Mesh.Get(meshName) + if not tempMesh.users: + containerMesh = tempMesh + if not containerMesh: + containerMesh = Mesh.New(temp_mesh_name) + del meshName + del tempMesh + + + # Initialize totals, these are updated each object totverts = totuvco = totno = 1 @@ -158,24 +266,30 @@ def save_obj(filename): globalNormals = {} # Get all meshs - for ob in scn.getChildren(): - #for ob in Object.GetSelected(): - try: - # Will work for non meshes now! :) - m = NMesh.GetRawFromObject(ob.name) - except: - continue + for ob in objects: - faces = [ f for f in m.faces if len(f) > 2 ] + # Will work for non meshes now! :) + if EXPORT_APPLY_MODIFIERS or ob.getType() != 'Mesh': + m = getMeshFromObject(ob, temp_mesh_name, containerMesh) + if not m: + continue + else: # We are a mesh. get the data. + m = ob.getData(mesh=1) - if not faces: # Make sure there is somthing to write + faces = [ f for f in m.faces ] + if EXPORT_EDGES: + edges = [ ed for ed in m.edges ] + else: + edges = [] + + if not (len(faces)+len(edges)): # Make sure there is somthing to write continue # dont bother with this mesh. m.transform(ob.matrix) # # Crash Blender #materials = m.getMaterials(1) # 1 == will return None in the list. - materials = m.getMaterials() + materials = m.materials if materials: @@ -190,7 +304,10 @@ def save_obj(filename): # Sort by Material, then images # so we dont over context switch in the obj file. - faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) + if m.faceUV: + faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) + else: + faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) # Set the default mat to no material and no image. @@ -204,38 +321,39 @@ def save_obj(filename): file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) # UV - if m.hasFaceUV(): + if m.faceUV: for f in faces: for uvKey in f.uv: + uvKey = tuple(uvKey) if not globalUVCoords.has_key(uvKey): globalUVCoords[uvKey] = totuvco totuvco +=1 file.write('vt %.6f %.6f 0.0\n' % uvKey) # NORMAL, Smooth/Non smoothed. - - for f in faces: - if f.smooth: - for v in f.v: - noKey = tuple(v.no) + if EXPORT_NORMALS: + for f in faces: + if f.smooth: + for v in f.v: + noKey = tuple(v.no) + if not globalNormals.has_key( noKey ): + globalNormals[noKey] = totno + totno +=1 + file.write('vn %.6f %.6f %.6f\n' % noKey) + else: + # Hard, 1 normal from the face. + noKey = tuple(f.no) if not globalNormals.has_key( noKey ): globalNormals[noKey] = totno totno +=1 file.write('vn %.6f %.6f %.6f\n' % noKey) - else: - # Hard, 1 normal from the face. - noKey = tuple(f.no) - if not globalNormals.has_key( noKey ): - globalNormals[noKey] = totno - totno +=1 - file.write('vn %.6f %.6f %.6f\n' % noKey) uvIdx = 0 for f in faces: # MAKE KEY - if f.image: # Object is always true. + if m.faceUV and f.image: # Object is always true. key = materialNames[f.mat], f.image.name else: key = materialNames[f.mat], None # No image, use None instead. @@ -279,57 +397,180 @@ def save_obj(filename): contextSmooth = f.smooth file.write('f') - if m.hasFaceUV(): - if f.smooth: # Smoothed, use vertex normals - for vi, v in enumerate(f.v): - file.write( ' %d/%d/%d' % (\ - v.index+totverts,\ - globalUVCoords[ f.uv[vi] ],\ - globalNormals[ tuple(v.no) ])) # vert, uv, normal - else: # No smoothing, face normals - no = globalNormals[ tuple(f.no) ] + if m.faceUV: + if EXPORT_NORMALS: + if f.smooth: # Smoothed, use vertex normals + for vi, v in enumerate(f.v): + file.write( ' %d/%d/%d' % (\ + v.index+totverts,\ + globalUVCoords[ tuple(f.uv[vi]) ],\ + globalNormals[ tuple(v.no) ])) # vert, uv, normal + else: # No smoothing, face normals + no = globalNormals[ tuple(f.no) ] + for vi, v in enumerate(f.v): + file.write( ' %d/%d/%d' % (\ + v.index+totverts,\ + globalUVCoords[ tuple(f.uv[vi]) ],\ + no)) # vert, uv, normal + + else: # No Normals for vi, v in enumerate(f.v): - file.write( ' %d/%d/%d' % (\ + file.write( ' %d/%d' % (\ v.index+totverts,\ - globalUVCoords[ f.uv[vi] ],\ - no)) # vert, uv, normal - + globalUVCoords[ tuple(f.uv[vi])])) # vert, uv + + else: # No UV's - if f.smooth: # Smoothed, use vertex normals - for v in f.v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - globalNormals[ tuple(v.no) ])) - else: # No smoothing, face normals - no = globalNormals[ tuple(f.no) ] + if EXPORT_NORMALS: + if f.smooth: # Smoothed, use vertex normals + for v in f.v: + file.write( ' %d//%d' % (\ + v.index+totverts,\ + globalNormals[ tuple(v.no) ])) + else: # No smoothing, face normals + no = globalNormals[ tuple(f.no) ] + for v in f.v: + file.write( ' %d//%d' % (\ + v.index+totverts,\ + no)) + else: # No Normals for v in f.v: - file.write( ' %d//%d' % (\ - v.index+totverts,\ - no)) + file.write( ' %d' % (\ + v.index+totverts)) file.write('\n') + # Write edges. + if EXPORT_EDGES: + edgeUsers = {} + for f in faces: + for i in xrange(len(f.v)): + faceEdgeVKey = sortPair(f.v[i].index, f.v[i-1].index) + + # We dont realy need to keep count. Just that a face uses it + # so dont export. + edgeUsers[faceEdgeVKey] = 1 + + for ed in edges: + edgeVKey = sortPair(ed.v1.index, ed.v2.index) + if not edgeUsers.has_key(edgeVKey): # No users? Write the edge. + file.write('f %d %d\n' % (edgeVKey[0]+totverts, edgeVKey[1]+totverts)) + # Make the indicies global rather then per mesh totverts += len(m.verts) file.close() # Now we have all our materials, save them - save_mtl(mtlfilename) + if EXPORT_MTL: + save_mtl(mtlfilename) + if EXPORT_COPY_IMAGES: + dest_dir = filename + # Remove chars until we are just the path. + while dest_dir and dest_dir[-1] not in '\\/': + dest_dir = dest_dir[:-1] + if dest_dir: + copy_images(dest_dir) + else: + print '\tError: "%s" could not be used as a base for an image path.' % filename + + print "OBJ Export time: %.2f" % (sys.time() - time1) + + + + + - print "obj export time: %.2f" % (sys.time() - time1) - -Window.FileSelector(save_obj, 'Export Wavefront OBJ', newFName('obj')) -''' -TIME = sys.time() -import os -OBJDIR = '/obj_out/' -for scn in Scene.Get(): - scn.makeCurrent() - obj = OBJDIR + scn.name - print obj - save_obj(obj) +def save_obj_ui(filename): + + for s in Window.GetScreenInfo(): + Window.QHandle(s['id']) + + EXPORT_APPLY_MODIFIERS = Draw.Create(1) + EXPORT_SEL_ONLY = Draw.Create(0) + EXPORT_EDGES = Draw.Create(0) + EXPORT_NORMALS = Draw.Create(0) + EXPORT_MTL = Draw.Create(1) + EXPORT_ALL_SCENES = Draw.Create(0) + EXPORT_ANIMATION = Draw.Create(0) + EXPORT_COPY_IMAGES = Draw.Create(0) + + + # Get USER Options + pup_block = [\ + ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\ + ('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection.'),\ + ('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\ + ('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\ + ('Materials', EXPORT_MTL, 'Write a seperate MTL file with the OBJ.'),\ + ('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a seperate OBJ file.'),\ + ('Animation', EXPORT_ANIMATION, 'Each frame as a seperate OBJ file.'),\ + ('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never everwrite.'),\ + ] + + if not Draw.PupBlock('Export...', pup_block): + return + + Window.WaitCursor(1) + + EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val + EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val + EXPORT_EDGES = EXPORT_EDGES.val + EXPORT_NORMALS = EXPORT_NORMALS.val + EXPORT_MTL = EXPORT_MTL.val + EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val + EXPORT_ANIMATION = EXPORT_ANIMATION.val + EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES.val + + + + base_name, ext = splitExt(filename) + context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension + + # Use the options to export the data using save_obj() + # def save_obj(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True): + orig_scene = Scene.GetCurrent() + if EXPORT_ALL_SCENES: + export_scenes = Scene.Get() + else: + export_scenes = [orig_scene] + + # Export all scenes. + for scn in export_scenes: + scn.makeCurrent() # If alredy current, this is not slow. + context = scn.getRenderingContext() + orig_frame = Blender.Get('curframe') + + if EXPORT_ALL_SCENES: # Add scene name into the context_name + context_name[1] = '_%s' % saneFilechars(scn.name) + + # Export an animation? + if EXPORT_ANIMATION: + scene_frames = range(context.startFrame(), context.endFrame()+1) # up to and including the end frame. + else: + scene_frames = [orig_frame] + + # Loop through all frames in the scene and export. + for frame in scene_frames: + if EXPORT_ANIMATION: # Add frame to the filename. + context_name[2] = '_%.6d' % frame + + Blender.Set('curframe', frame) + if EXPORT_SEL_ONLY: + export_objects = Object.GetSelected() # Export Context + else: + export_objects = scn.getChildren() + + # EXPORTTHE FILE. + save_obj(''.join(context_name), export_objects, EXPORT_EDGES, EXPORT_NORMALS, EXPORT_MTL, EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS) + + Blender.Set('curframe', orig_frame) + + # Restore old active scene. + orig_scene.makeCurrent() + Window.WaitCursor(0) + + -print "TOTAL EXPORT TIME: ", sys.time() - TIME -''' +Window.FileSelector(save_obj_ui, 'Export Wavefront OBJ', newFName('obj')) diff --git a/release/scripts/obj_import.py b/release/scripts/obj_import.py index d8788f6d042..950eebb7c00 100644 --- a/release/scripts/obj_import.py +++ b/release/scripts/obj_import.py @@ -377,9 +377,7 @@ def load_mtl(dir, mtl_file, meshDict, materialDict): l = fileLines[lIdx].split() # Detect a line that will be ignored - if len(l) == 0: - pass - elif l[0] == '#' or len(l) == 0: + if len(l) == 0 or l[0].startswith('#'): pass elif l[0] == 'newmtl': currentMat = getMat('_'.join(l[1:]), materialDict) # Material should alredy exist. @@ -683,6 +681,18 @@ def load_obj(file): badObjUvs +=1 # ERROR, Cont # Quads only, we could import quads using the method below but it polite to import a quad as a quad. + if len(vIdxLs) == 2: + # Edge + for i in (0,1): + if currentUsedVertListSmoothGroup[vIdxLs[i]] == 0: + faceQuadVList[i] = vertList[vIdxLs[i]] + currentMesh.verts.append(faceQuadVList[i]) + currentUsedVertListSmoothGroup[vIdxLs[i]] = len(currentMesh.verts)-1 + else: + faceQuadVList[i] = currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]] + + currentMesh.addEdge(faceQuadVList[0], faceQuadVList[1]) + if len(vIdxLs) == 4: # Have found some files where wach face references the same vert |