2004-06-21 12:01:23 +00:00
#!BPY
2004-06-07 01:34:15 +00:00
2004-06-21 12:01:23 +00:00
"""
Name : ' Wavefront (.obj)... '
2008-10-19 15:53:22 +00:00
Blender : 248
2004-06-08 04:43:40 +00:00
Group : ' Export '
2004-06-21 12:01:23 +00:00
Tooltip : ' Save a Wavefront OBJ File '
"""
2004-06-07 01:34:15 +00:00
2004-12-12 13:42:49 +00:00
__author__ = " Campbell Barton, Jiri Hnidek "
2008-10-19 15:53:22 +00:00
__url__ = [ ' http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj ' , ' www.blender.org ' , ' blenderartists.org ' ]
__version__ = " 1.2 "
2004-11-07 16:31:13 +00:00
__bpydoc__ = """ \
This script is an exporter to OBJ file format .
Usage :
2006-09-25 05:12:37 +00:00
Select the objects you wish to export and run this script from " File->Export " menu .
Selecting the default options from the popup box will be good in most cases .
All objects that can be represented as a mesh ( mesh , curve , metaball , surface , text3d )
will be exported as mesh data .
2004-11-07 16:31:13 +00:00
"""
2004-06-21 12:01:23 +00:00
# --------------------------------------------------------------------------
2007-01-26 07:01:30 +00:00
# OBJ Export v1.1 by Campbell Barton (AKA Ideasman)
2004-06-21 12:01:23 +00:00
# --------------------------------------------------------------------------
# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
2005-05-30 02:26:40 +00:00
2006-01-29 19:17:53 +00:00
import Blender
from Blender import Mesh , Scene , Window , sys , Image , Draw
2006-04-07 00:47:39 +00:00
import BPyMesh
2006-10-01 14:02:51 +00:00
import BPyObject
2007-04-19 20:58:09 +00:00
import BPySys
2006-09-25 05:12:37 +00:00
import BPyMessages
2006-01-29 19:17:53 +00:00
# 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 : ]
2004-06-21 12:01:23 +00:00
2005-10-11 02:32:58 +00:00
def fixName ( name ) :
if name == None :
return ' None '
else :
return name . replace ( ' ' , ' _ ' )
# A Dict of Materials
# (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
MTL_DICT = { }
2004-06-21 12:01:23 +00:00
2006-01-29 19:17:53 +00:00
def write_mtl ( filename ) :
2005-10-11 02:32:58 +00:00
2006-01-29 19:17:53 +00:00
world = Blender . World . GetCurrent ( )
2005-10-11 02:32:58 +00:00
if world :
worldAmb = world . getAmb ( )
else :
worldAmb = ( 0 , 0 , 0 ) # Default value
2004-06-21 12:01:23 +00:00
file = open ( filename , " w " )
2006-04-07 00:47:39 +00:00
file . write ( ' # Blender3D MTL File: %s \n ' % Blender . Get ( ' filename ' ) . split ( ' \\ ' ) [ - 1 ] . split ( ' / ' ) [ - 1 ] )
2005-10-11 02:32:58 +00:00
file . write ( ' # Material Count: %i \n ' % len ( MTL_DICT ) )
# Write material/image combinations we have used.
2007-06-04 08:15:27 +00:00
for key , ( mtl_mat_name , mat , img ) in MTL_DICT . iteritems ( ) :
2005-10-11 02:32:58 +00:00
# Get the Blender data for the material and the image.
# Having an image named None will make a bug, dont do it :)
file . write ( ' newmtl %s \n ' % mtl_mat_name ) # Define a new material: matname_imgname
2007-06-04 08:15:27 +00:00
if mat :
2006-04-07 00:47:39 +00:00
file . write ( ' Ns %.6f \n ' % ( ( mat . getHardness ( ) - 1 ) * 1.9607843137254901 ) ) # Hardness, convert blenders 1-511 to MTL's
2007-01-26 06:02:21 +00:00
file . write ( ' Ka %.6f %.6f %.6f \n ' % tuple ( [ c * mat . amb for c in worldAmb ] ) ) # Ambient, uses mirror colour,
file . write ( ' Kd %.6f %.6f %.6f \n ' % tuple ( [ c * mat . ref for c in mat . rgbCol ] ) ) # Diffuse
file . write ( ' Ks %.6f %.6f %.6f \n ' % tuple ( [ c * mat . spec for c in mat . specCol ] ) ) # Specular
file . write ( ' Ni %.6f \n ' % mat . IOR ) # Refraction index
file . write ( ' d %.6f \n ' % mat . alpha ) # Alpha (obj uses 'd' for dissolve)
2005-10-11 02:32:58 +00:00
# 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting.
2006-01-29 19:17:53 +00:00
if mat . getMode ( ) & Blender . Material . Modes [ ' SHADELESS ' ] :
2005-10-11 02:32:58 +00:00
file . write ( ' illum 0 \n ' ) # ignore lighting
elif mat . getSpec ( ) == 0 :
file . write ( ' illum 1 \n ' ) # no specular.
else :
file . write ( ' illum 2 \n ' ) # light normaly
2007-06-04 08:15:27 +00:00
else :
#write a dummy material here?
file . write ( ' Ns 0 \n ' )
file . write ( ' Ka %.6f %.6f %.6f \n ' % tuple ( [ c for c in worldAmb ] ) ) # Ambient, uses mirror colour,
file . write ( ' Kd 0.8 0.8 0.8 \n ' )
file . write ( ' Ks 0.8 0.8 0.8 \n ' )
file . write ( ' d 1 \n ' ) # No alpha
file . write ( ' illum 2 \n ' ) # light normaly
2005-10-11 02:32:58 +00:00
# Write images!
2007-06-04 08:15:27 +00:00
if img : # We have an image on the face!
2005-10-11 02:32:58 +00:00
file . write ( ' map_Kd %s \n ' % img . filename . split ( ' \\ ' ) [ - 1 ] . split ( ' / ' ) [ - 1 ] ) # Diffuse mapping image
2007-06-04 08:15:27 +00:00
elif not mat : # No face image. if we havea material search for MTex image.
2005-10-11 02:32:58 +00:00
for mtex in mat . getTextures ( ) :
2006-01-29 19:17:53 +00:00
if mtex and mtex . tex . type == Blender . Texture . Types . IMAGE :
2005-10-11 02:32:58 +00:00
try :
filename = mtex . tex . image . filename . split ( ' \\ ' ) [ - 1 ] . split ( ' / ' ) [ - 1 ]
file . write ( ' map_Kd %s \n ' % filename ) # Diffuse mapping image
break
except :
# Texture has no image though its an image type, best ignore.
pass
file . write ( ' \n \n ' )
2004-06-21 12:01:23 +00:00
file . close ( )
2006-01-29 19:17:53 +00:00
def copy_file ( source , dest ) :
file = open ( source , ' rb ' )
data = file . read ( )
file . close ( )
file = open ( dest , ' wb ' )
file . write ( data )
file . close ( )
2005-10-11 02:32:58 +00:00
2006-01-29 19:17:53 +00:00
def copy_images ( dest_dir ) :
if dest_dir [ - 1 ] != sys . sep :
dest_dir + = sys . sep
2005-10-11 02:32:58 +00:00
2006-01-29 19:17:53 +00:00
# Get unique image names
uniqueImages = { }
2007-06-04 08:15:27 +00:00
for matname , mat , image in MTL_DICT . itervalues ( ) : # Only use image name
2006-04-07 00:47:39 +00:00
# Get Texface images
2007-06-04 08:15:27 +00:00
if image :
uniqueImages [ image ] = image # Should use sets here. wait until Python 2.4 is default.
2006-04-07 00:47:39 +00:00
# Get MTex images
2007-06-04 08:15:27 +00:00
if mat :
2006-12-25 10:53:00 +00:00
for mtex in mat . getTextures ( ) :
2006-04-10 21:42:18 +00:00
if mtex and mtex . tex . type == Blender . Texture . Types . IMAGE :
2007-06-04 08:15:27 +00:00
image_tex = mtex . tex . image
if image_tex :
try :
uniqueImages [ image_tex ] = image_tex
except :
pass
2006-01-29 19:17:53 +00:00
# Now copy images
copyCount = 0
2007-06-04 08:15:27 +00:00
for bImage in uniqueImages . itervalues ( ) :
2006-01-29 19:17:53 +00:00
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 ' \t Copying " %s " > " %s " ' % ( image_path , dest_image_path )
copy_file ( image_path , dest_image_path )
copyCount + = 1
print ' \t Copied %d images ' % copyCount
2006-07-06 12:25:04 +00:00
2006-01-29 19:17:53 +00:00
def write ( filename , objects , \
2006-07-06 12:25:04 +00:00
EXPORT_TRI = False , EXPORT_EDGES = False , EXPORT_NORMALS = False , EXPORT_NORMALS_HQ = False , \
2006-01-29 19:17:53 +00:00
EXPORT_UV = True , EXPORT_MTL = True , EXPORT_COPY_IMAGES = False , \
2006-08-06 11:32:12 +00:00
EXPORT_APPLY_MODIFIERS = True , EXPORT_ROTX90 = True , EXPORT_BLEN_OBS = True , \
2008-09-17 04:07:58 +00:00
EXPORT_GROUP_BY_OB = False , EXPORT_GROUP_BY_MAT = False , EXPORT_KEEP_VERT_ORDER = False ) :
2006-01-29 19:17:53 +00:00
'''
Basic write function . The context and options must be alredy set
This can be accessed externaly
eg .
write ( ' c: \\ test \\ foobar.obj ' , Blender . Object . GetSelected ( ) ) # Using default options.
'''
2007-06-04 08:15:27 +00:00
def veckey3d ( v ) :
return round ( v . x , 6 ) , round ( v . y , 6 ) , round ( v . z , 6 )
2008-02-27 23:10:25 +00:00
def veckey2d ( v ) :
return round ( v . x , 6 ) , round ( v . y , 6 )
2007-06-04 08:15:27 +00:00
2006-01-29 19:17:53 +00:00
print ' OBJ Export path: " %s " ' % filename
temp_mesh_name = ' ~tmp-mesh '
2006-04-03 16:14:24 +00:00
2004-09-19 10:41:04 +00:00
time1 = sys . time ( )
2005-05-30 02:26:40 +00:00
scn = Scene . GetCurrent ( )
2004-06-21 12:01:23 +00:00
2004-09-19 10:41:04 +00:00
file = open ( filename , " w " )
2005-08-01 03:06:24 +00:00
2004-09-19 10:41:04 +00:00
# Write Header
2006-09-25 05:12:37 +00:00
file . write ( ' # Blender3D v %s OBJ File: %s \n ' % ( Blender . Get ( ' version ' ) , Blender . Get ( ' filename ' ) . split ( ' / ' ) [ - 1 ] . split ( ' \\ ' ) [ - 1 ] ) )
2006-01-29 19:17:53 +00:00
file . write ( ' # www.blender3d.org \n ' )
2004-09-19 10:41:04 +00:00
# Tell the obj file what material file to use.
2007-01-26 07:01:30 +00:00
if EXPORT_MTL :
mtlfilename = ' %s .mtl ' % ' . ' . join ( filename . split ( ' . ' ) [ : - 1 ] )
file . write ( ' mtllib %s \n ' % ( mtlfilename . split ( ' \\ ' ) [ - 1 ] . split ( ' / ' ) [ - 1 ] ) )
2006-01-29 19:17:53 +00:00
2006-04-02 01:46:10 +00:00
# Get the container mesh. - used for applying modifiers and non mesh objects.
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 )
2006-10-01 14:02:51 +00:00
if EXPORT_ROTX90 :
mat_xrot90 = Blender . Mathutils . RotationMatrix ( - 90 , 4 , ' x ' )
2006-04-02 01:46:10 +00:00
del meshName
2006-10-01 14:02:51 +00:00
del tempMesh
2006-01-29 19:17:53 +00:00
2004-09-19 10:41:04 +00:00
# Initialize totals, these are updated each object
2005-10-11 02:32:58 +00:00
totverts = totuvco = totno = 1
2005-05-30 02:26:40 +00:00
2008-02-27 23:10:25 +00:00
face_vert_index = 1
2007-01-26 06:02:21 +00:00
2005-10-11 02:32:58 +00:00
globalNormals = { }
2005-08-01 03:06:24 +00:00
2004-09-19 10:41:04 +00:00
# Get all meshs
2006-10-01 14:02:51 +00:00
for ob_main in objects :
for ob , ob_mat in BPyObject . getDerivedObjects ( ob_main ) :
# Will work for non meshes now! :)
# getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None)
me = BPyMesh . getMeshFromObject ( ob , containerMesh , EXPORT_APPLY_MODIFIERS , False , scn )
if not me :
continue
2008-02-27 23:10:25 +00:00
if EXPORT_UV :
faceuv = me . faceUV
else :
faceuv = False
2006-01-29 19:17:53 +00:00
2006-10-01 14:02:51 +00:00
# We have a valid mesh
if EXPORT_TRI and me . faces :
# Add a dummy object to it.
2007-01-25 04:18:09 +00:00
has_quads = False
2006-10-01 14:02:51 +00:00
for f in me . faces :
if len ( f ) == 4 :
2007-01-25 04:18:09 +00:00
has_quads = True
break
2006-10-01 14:02:51 +00:00
2007-01-25 04:18:09 +00:00
if has_quads :
2007-01-26 06:02:21 +00:00
oldmode = Mesh . Mode ( )
Mesh . Mode ( Mesh . SelectModes [ ' FACE ' ] )
2007-01-25 04:18:09 +00:00
me . sel = True
tempob = scn . objects . new ( me )
2006-10-01 14:02:51 +00:00
me . quadToTriangle ( 0 ) # more=0 shortest length
oldmode = Mesh . Mode ( oldmode )
2007-01-25 04:18:09 +00:00
scn . objects . unlink ( tempob )
2007-01-26 06:02:21 +00:00
Mesh . Mode ( oldmode )
2006-01-29 19:17:53 +00:00
2006-10-01 14:02:51 +00:00
# Make our own list so it can be sorted to reduce context switching
faces = [ f for f in me . faces ]
if EXPORT_EDGES :
edges = me . edges
else :
edges = [ ]
if not ( len ( faces ) + len ( edges ) + len ( me . verts ) ) : # Make sure there is somthing to write
continue # dont bother with this mesh.
if EXPORT_ROTX90 :
me . transform ( ob_mat * mat_xrot90 )
else :
me . transform ( ob_mat )
# High Quality Normals
2007-06-06 04:34:59 +00:00
if EXPORT_NORMALS and faces :
if EXPORT_NORMALS_HQ :
BPyMesh . meshCalcNormals ( me )
else :
# transforming normals is incorrect
# when the matrix is scaled,
# better to recalculate them
me . calcNormals ( )
2006-10-01 14:02:51 +00:00
# # Crash Blender
#materials = me.getMaterials(1) # 1 == will return None in the list.
materials = me . materials
materialNames = [ ]
2007-06-04 08:15:27 +00:00
materialItems = materials [ : ]
2006-10-01 14:02:51 +00:00
if materials :
for mat in materials :
if mat : # !=None
materialNames . append ( mat . name )
else :
materialNames . append ( None )
# Cant use LC because some materials are None.
# materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken.
# Possible there null materials, will mess up indicies
# but at least it will export, wait until Blender gets fixed.
materialNames . extend ( ( 16 - len ( materialNames ) ) * [ None ] )
2007-06-04 08:15:27 +00:00
materialItems . extend ( ( 16 - len ( materialItems ) ) * [ None ] )
2006-10-01 14:02:51 +00:00
# Sort by Material, then images
# so we dont over context switch in the obj file.
2008-09-17 04:07:58 +00:00
if EXPORT_KEEP_VERT_ORDER :
2007-01-26 07:01:30 +00:00
pass
2008-02-27 23:10:25 +00:00
elif faceuv :
2007-01-25 04:18:09 +00:00
try : faces . sort ( key = lambda a : ( a . mat , a . image , a . smooth ) )
except : faces . sort ( lambda a , b : cmp ( ( a . mat , a . image , a . smooth ) , ( b . mat , b . image , b . smooth ) ) )
2006-10-01 14:02:51 +00:00
elif len ( materials ) > 1 :
2007-01-25 04:18:09 +00:00
try : faces . sort ( key = lambda a : ( a . mat , a . smooth ) )
except : faces . sort ( lambda a , b : cmp ( ( a . mat , a . smooth ) , ( b . mat , b . smooth ) ) )
2006-10-01 14:02:51 +00:00
else :
# no materials
2007-01-25 04:18:09 +00:00
try : faces . sort ( key = lambda a : a . smooth )
except : faces . sort ( lambda a , b : cmp ( a . smooth , b . smooth ) )
2006-10-01 14:02:51 +00:00
# Set the default mat to no material and no image.
contextMat = ( 0 , 0 ) # Can never be this, so we will label a new material teh first chance we get.
contextSmooth = None # Will either be true or false, set bad to force initialization switch.
if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB :
2007-01-31 01:18:51 +00:00
name1 = ob . name
name2 = ob . getData ( 1 )
if name1 == name2 :
obnamestring = fixName ( name1 )
else :
obnamestring = ' %s _ %s ' % ( fixName ( name1 ) , fixName ( name2 ) )
2006-10-01 14:02:51 +00:00
if EXPORT_BLEN_OBS :
file . write ( ' o %s \n ' % obnamestring ) # Write Object name
else : # if EXPORT_GROUP_BY_OB:
file . write ( ' g %s \n ' % obnamestring )
# Vert
for v in me . verts :
2006-08-06 11:32:12 +00:00
file . write ( ' v %.6f %.6f %.6f \n ' % tuple ( v . co ) )
2006-10-01 14:02:51 +00:00
# UV
2008-02-27 23:10:25 +00:00
if faceuv :
uv_face_mapping = [ [ 0 , 0 , 0 , 0 ] for f in faces ] # a bit of a waste for tri's :/
uv_dict = { } # could use a set() here
for f_index , f in enumerate ( faces ) :
for uv_index , uv in enumerate ( f . uv ) :
uvkey = veckey2d ( uv )
try :
uv_face_mapping [ f_index ] [ uv_index ] = uv_dict [ uvkey ]
except :
uv_face_mapping [ f_index ] [ uv_index ] = uv_dict [ uvkey ] = len ( uv_dict )
file . write ( ' vt %.6f %.6f \n ' % tuple ( uv ) )
uv_unique_count = len ( uv_dict )
del uv , uvkey , uv_dict , f_index , uv_index
# Only need uv_unique_count and uv_face_mapping
2006-10-01 14:02:51 +00:00
# NORMAL, Smooth/Non smoothed.
if EXPORT_NORMALS :
for f in faces :
if f . smooth :
2006-12-12 07:28:20 +00:00
for v in f :
2006-10-01 14:02:51 +00:00
noKey = veckey3d ( 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 = veckey3d ( f . no )
2006-01-29 19:17:53 +00:00
if not globalNormals . has_key ( noKey ) :
globalNormals [ noKey ] = totno
totno + = 1
2006-08-06 11:32:12 +00:00
file . write ( ' vn %.6f %.6f %.6f \n ' % noKey )
2008-02-27 23:10:25 +00:00
2007-06-04 08:15:27 +00:00
if not faceuv :
f_image = None
2005-10-11 02:32:58 +00:00
2008-02-27 23:10:25 +00:00
for f_index , f in enumerate ( faces ) :
2006-10-01 14:02:51 +00:00
f_v = f . v
2007-06-04 08:15:27 +00:00
f_smooth = f . smooth
f_mat = min ( f . mat , len ( materialNames ) - 1 )
2006-10-01 14:02:51 +00:00
if faceuv :
2007-06-04 08:15:27 +00:00
f_image = f . image
2006-10-01 14:02:51 +00:00
f_uv = f . uv
# MAKE KEY
2008-02-27 23:10:25 +00:00
if faceuv and f_image : # Object is always true.
2007-06-04 08:15:27 +00:00
key = materialNames [ f_mat ] , f_image . name
2006-01-29 19:17:53 +00:00
else :
2007-06-04 08:15:27 +00:00
key = materialNames [ f_mat ] , None # No image, use None instead.
2006-10-01 14:02:51 +00:00
# CHECK FOR CONTEXT SWITCH
if key == contextMat :
pass # Context alredy switched, dont do anythoing
else :
if key [ 0 ] == None and key [ 1 ] == None :
# Write a null material, since we know the context has changed.
2007-06-04 08:15:27 +00:00
if EXPORT_GROUP_BY_MAT :
file . write ( ' g %s _ %s \n ' % ( fixName ( ob . name ) , fixName ( ob . getData ( 1 ) ) ) ) # can be mat_image or (null)
2006-10-01 14:02:51 +00:00
file . write ( ' usemtl (null) \n ' ) # mat, image
2006-01-29 19:17:53 +00:00
2006-10-01 14:02:51 +00:00
else :
2007-06-04 08:15:27 +00:00
mat_data = MTL_DICT . get ( key )
if not mat_data :
2006-10-01 14:02:51 +00:00
# First add to global dict so we can export to mtl
# Then write mtl
# Make a new names from the mat and image name,
# converting any spaces to underscores with fixName.
# If none image dont bother adding it to the name
if key [ 1 ] == None :
2007-06-04 08:15:27 +00:00
mat_data = MTL_DICT [ key ] = ( ' %s ' % fixName ( key [ 0 ] ) ) , materialItems [ f_mat ] , f_image
2006-10-01 14:02:51 +00:00
else :
2007-06-04 08:15:27 +00:00
mat_data = MTL_DICT [ key ] = ( ' %s _ %s ' % ( fixName ( key [ 0 ] ) , fixName ( key [ 1 ] ) ) ) , materialItems [ f_mat ] , f_image
if EXPORT_GROUP_BY_MAT :
file . write ( ' g %s _ %s _ %s \n ' % ( fixName ( ob . name ) , fixName ( ob . getData ( 1 ) ) , mat_data [ 0 ] ) ) # can be mat_image or (null)
file . write ( ' usemtl %s \n ' % mat_data [ 0 ] ) # can be mat_image or (null)
2006-10-01 14:02:51 +00:00
contextMat = key
2007-06-04 08:15:27 +00:00
if f_smooth != contextSmooth :
if f_smooth : # on now off
2006-10-01 14:02:51 +00:00
file . write ( ' s 1 \n ' )
2007-06-04 08:15:27 +00:00
contextSmooth = f_smooth
else : # was off now on
file . write ( ' s off \n ' )
contextSmooth = f_smooth
2005-10-11 02:32:58 +00:00
2006-10-01 14:02:51 +00:00
file . write ( ' f ' )
2008-02-27 23:10:25 +00:00
if faceuv :
2006-10-01 14:02:51 +00:00
if EXPORT_NORMALS :
2007-06-04 08:15:27 +00:00
if f_smooth : # Smoothed, use vertex normals
2006-10-01 14:02:51 +00:00
for vi , v in enumerate ( f_v ) :
file . write ( ' %d / %d / %d ' % ( \
v . index + totverts , \
2008-02-27 23:10:25 +00:00
totuvco + uv_face_mapping [ f_index ] [ vi ] , \
2006-10-01 14:02:51 +00:00
globalNormals [ veckey3d ( v . no ) ] ) ) # vert, uv, normal
2007-01-26 06:02:21 +00:00
2006-10-01 14:02:51 +00:00
else : # No smoothing, face normals
no = globalNormals [ veckey3d ( f . no ) ]
for vi , v in enumerate ( f_v ) :
file . write ( ' %d / %d / %d ' % ( \
v . index + totverts , \
2008-02-27 23:10:25 +00:00
totuvco + uv_face_mapping [ f_index ] [ vi ] , \
2006-10-01 14:02:51 +00:00
no ) ) # vert, uv, normal
else : # No Normals
2006-06-07 02:10:10 +00:00
for vi , v in enumerate ( f_v ) :
2006-10-01 14:02:51 +00:00
file . write ( ' %d / %d ' % ( \
2006-01-29 19:17:53 +00:00
v . index + totverts , \
2008-02-27 23:10:25 +00:00
totuvco + uv_face_mapping [ f_index ] [ vi ] ) ) # vert, uv
2007-01-26 06:02:21 +00:00
face_vert_index + = len ( f_v )
2006-10-01 14:02:51 +00:00
else : # No UV's
if EXPORT_NORMALS :
2007-06-04 08:15:27 +00:00
if f_smooth : # Smoothed, use vertex normals
2006-10-01 14:02:51 +00:00
for v in f_v :
file . write ( ' %d // %d ' % ( \
v . index + totverts , \
globalNormals [ veckey3d ( v . no ) ] ) )
else : # No smoothing, face normals
no = globalNormals [ veckey3d ( f . no ) ]
for v in f_v :
file . write ( ' %d // %d ' % ( \
v . index + totverts , \
no ) )
else : # No Normals
2006-06-07 02:10:10 +00:00
for v in f_v :
2006-10-01 14:02:51 +00:00
file . write ( ' %d ' % ( \
v . index + totverts ) )
file . write ( ' \n ' )
2006-09-25 05:12:37 +00:00
2006-10-01 14:02:51 +00:00
# Write edges.
if EXPORT_EDGES :
LOOSE = Mesh . EdgeFlags . LOOSE
for ed in edges :
if ed . flag & LOOSE :
file . write ( ' f %d %d \n ' % ( ed . v1 . index + totverts , ed . v2 . index + totverts ) )
# Make the indicies global rather then per mesh
totverts + = len ( me . verts )
2008-02-27 23:10:25 +00:00
if faceuv :
totuvco + = uv_unique_count
2006-10-01 14:02:51 +00:00
me . verts = None
2004-09-19 10:41:04 +00:00
file . close ( )
2005-10-11 02:32:58 +00:00
# Now we have all our materials, save them
2006-01-29 19:17:53 +00:00
if EXPORT_MTL :
write_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 ' \t Error: " %s " could not be used as a base for an image path. ' % filename
print " OBJ Export time: %.2f " % ( sys . time ( ) - time1 )
2005-10-11 02:32:58 +00:00
2004-08-04 06:16:46 +00:00
2006-01-29 19:17:53 +00:00
def write_ui ( filename ) :
2007-01-25 04:18:09 +00:00
if not filename . lower ( ) . endswith ( ' .obj ' ) :
filename + = ' .obj '
2007-08-21 19:37:03 +00:00
if not BPyMessages . Warning_SaveOver ( filename ) :
return
2007-01-25 04:18:09 +00:00
2008-10-19 15:53:22 +00:00
global EXPORT_APPLY_MODIFIERS , EXPORT_ROTX90 , EXPORT_TRI , EXPORT_EDGES , \
EXPORT_NORMALS , EXPORT_NORMALS_HQ , EXPORT_UV , \
EXPORT_MTL , EXPORT_SEL_ONLY , EXPORT_ALL_SCENES , \
EXPORT_ANIMATION , EXPORT_COPY_IMAGES , EXPORT_BLEN_OBS , \
EXPORT_GROUP_BY_OB , EXPORT_GROUP_BY_MAT , EXPORT_KEEP_VERT_ORDER
EXPORT_APPLY_MODIFIERS = Draw . Create ( 0 )
2006-08-06 11:32:12 +00:00
EXPORT_ROTX90 = Draw . Create ( 1 )
2006-01-29 19:17:53 +00:00
EXPORT_TRI = Draw . Create ( 0 )
2006-10-01 14:02:51 +00:00
EXPORT_EDGES = Draw . Create ( 1 )
2006-01-29 19:17:53 +00:00
EXPORT_NORMALS = Draw . Create ( 0 )
2008-10-19 15:53:22 +00:00
EXPORT_NORMALS_HQ = Draw . Create ( 0 )
2006-01-29 19:17:53 +00:00
EXPORT_UV = Draw . Create ( 1 )
EXPORT_MTL = Draw . Create ( 1 )
EXPORT_SEL_ONLY = Draw . Create ( 1 )
EXPORT_ALL_SCENES = Draw . Create ( 0 )
EXPORT_ANIMATION = Draw . Create ( 0 )
EXPORT_COPY_IMAGES = Draw . Create ( 0 )
2008-10-19 15:53:22 +00:00
EXPORT_BLEN_OBS = Draw . Create ( 0 )
2006-01-29 19:17:53 +00:00
EXPORT_GROUP_BY_OB = Draw . Create ( 0 )
EXPORT_GROUP_BY_MAT = Draw . Create ( 0 )
2008-09-17 04:07:58 +00:00
EXPORT_KEEP_VERT_ORDER = Draw . Create ( 1 )
2007-01-26 07:01:30 +00:00
2008-10-19 15:53:22 +00:00
# Old UI
'''
2007-01-26 07:01:30 +00:00
# removed too many options are bad!
2006-01-29 19:17:53 +00:00
# Get USER Options
pup_block = [ \
2006-12-15 22:14:33 +00:00
( ' Context... ' ) , \
( ' Selection Only ' , EXPORT_SEL_ONLY , ' Only export objects in visible selection. Else export whole scene. ' ) , \
2006-12-16 10:39:08 +00:00
( ' All Scenes ' , EXPORT_ALL_SCENES , ' Each scene as a separate OBJ file. ' ) , \
2006-12-15 22:14:33 +00:00
( ' Animation ' , EXPORT_ANIMATION , ' Each frame as a numbered OBJ file. ' ) , \
( ' Object Prefs... ' ) , \
2006-01-29 19:17:53 +00:00
( ' Apply Modifiers ' , EXPORT_APPLY_MODIFIERS , ' Use transformed mesh data from each object. May break vert order for morph targets. ' ) , \
2006-08-06 11:32:12 +00:00
( ' Rotate X90 ' , EXPORT_ROTX90 , ' Rotate on export so Blenders UP is translated into OBJs UP ' ) , \
2008-09-17 04:07:58 +00:00
( ' Keep Vert Order ' , EXPORT_KEEP_VERT_ORDER , ' Keep vert and face order, disables some other options. ' ) , \
2006-12-15 22:14:33 +00:00
( ' Extra Data... ' ) , \
2006-01-29 19:17:53 +00:00
( ' Edges ' , EXPORT_EDGES , ' Edges not connected to faces. ' ) , \
( ' Normals ' , EXPORT_NORMALS , ' Export vertex normal data (Ignored on import). ' ) , \
2006-07-06 12:25:04 +00:00
( ' High Quality Normals ' , EXPORT_NORMALS_HQ , ' Calculate high quality normals for rendering. ' ) , \
2006-01-29 19:17:53 +00:00
( ' UVs ' , EXPORT_UV , ' Export texface UV coords. ' ) , \
2006-04-03 21:48:18 +00:00
( ' Materials ' , EXPORT_MTL , ' Write a separate MTL file with the OBJ. ' ) , \
2006-01-29 19:17:53 +00:00
( ' Copy Images ' , EXPORT_COPY_IMAGES , ' Copy image files to the export directory, never overwrite. ' ) , \
2006-12-15 22:14:33 +00:00
( ' Triangulate ' , EXPORT_TRI , ' Triangulate quads. ' ) , \
2006-01-29 19:17:53 +00:00
( ' Grouping... ' ) , \
2006-07-06 12:25:04 +00:00
( ' Objects ' , EXPORT_BLEN_OBS , ' Export blender objects as " OBJ objects " . ' ) , \
( ' Object Groups ' , EXPORT_GROUP_BY_OB , ' Export blender objects as " OBJ Groups " . ' ) , \
2006-01-29 19:17:53 +00:00
( ' Material Groups ' , EXPORT_GROUP_BY_MAT , ' Group by materials. ' ) , \
]
if not Draw . PupBlock ( ' Export... ' , pup_block ) :
return
2008-10-19 15:53:22 +00:00
'''
# BEGIN ALTERNATIVE UI *******************
if True :
EVENT_NONE = 0
EVENT_EXIT = 1
EVENT_REDRAW = 2
EVENT_EXPORT = 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 EXPORT_BLEN_OBS , EXPORT_GROUP_BY_OB , EXPORT_GROUP_BY_MAT , EXPORT_APPLY_MODIFIERS , KEEP_VERT_ORDER
if EXPORT_BLEN_OBS . val or EXPORT_GROUP_BY_OB . val or EXPORT_GROUP_BY_MAT . val or EXPORT_APPLY_MODIFIERS . val :
EXPORT_KEEP_VERT_ORDER . val = 0
else :
EXPORT_KEEP_VERT_ORDER . val = 1
def do_vertorder ( e , v ) :
global EXPORT_BLEN_OBS , EXPORT_GROUP_BY_OB , EXPORT_GROUP_BY_MAT , EXPORT_APPLY_MODIFIERS , KEEP_VERT_ORDER
if EXPORT_KEEP_VERT_ORDER . val :
EXPORT_BLEN_OBS . val = EXPORT_GROUP_BY_OB . val = EXPORT_GROUP_BY_MAT . val = EXPORT_APPLY_MODIFIERS . val = 0
else :
if not ( EXPORT_BLEN_OBS . val or EXPORT_GROUP_BY_OB . val or EXPORT_GROUP_BY_MAT . val or EXPORT_APPLY_MODIFIERS . val ) :
EXPORT_KEEP_VERT_ORDER . val = 1
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 - = 110
global EXPORT_APPLY_MODIFIERS , EXPORT_ROTX90 , EXPORT_TRI , EXPORT_EDGES , \
EXPORT_NORMALS , EXPORT_NORMALS_HQ , EXPORT_UV , \
EXPORT_MTL , EXPORT_SEL_ONLY , EXPORT_ALL_SCENES , \
EXPORT_ANIMATION , EXPORT_COPY_IMAGES , EXPORT_BLEN_OBS , \
EXPORT_GROUP_BY_OB , EXPORT_GROUP_BY_MAT , EXPORT_KEEP_VERT_ORDER
Draw . Label ( ' Context... ' , ui_x + 9 , ui_y + 209 , 220 , 20 )
Draw . BeginAlign ( )
EXPORT_SEL_ONLY = Draw . Toggle ( ' Selection Only ' , EVENT_NONE , ui_x + 9 , ui_y + 189 , 110 , 20 , EXPORT_SEL_ONLY . val , ' Only export objects in visible selection. Else export whole scene. ' )
EXPORT_ALL_SCENES = Draw . Toggle ( ' All Scenes ' , EVENT_NONE , ui_x + 119 , ui_y + 189 , 110 , 20 , EXPORT_ALL_SCENES . val , ' Each scene as a separate OBJ file. ' )
EXPORT_ANIMATION = Draw . Toggle ( ' Animation ' , EVENT_NONE , ui_x + 229 , ui_y + 189 , 110 , 20 , EXPORT_ANIMATION . val , ' Each frame as a numbered OBJ file. ' )
Draw . EndAlign ( )
Draw . Label ( ' Output Options... ' , ui_x + 9 , ui_y + 159 , 220 , 20 )
Draw . BeginAlign ( )
EXPORT_APPLY_MODIFIERS = Draw . Toggle ( ' Apply Modifiers ' , EVENT_REDRAW , ui_x + 9 , ui_y + 140 , 110 , 20 , EXPORT_APPLY_MODIFIERS . val , ' Use transformed mesh data from each object. May break vert order for morph targets. ' , do_split )
EXPORT_ROTX90 = Draw . Toggle ( ' Rotate X90 ' , EVENT_NONE , ui_x + 119 , ui_y + 140 , 110 , 20 , EXPORT_ROTX90 . val , ' Rotate on export so Blenders UP is translated into OBJs UP ' )
EXPORT_COPY_IMAGES = Draw . Toggle ( ' Copy Images ' , EVENT_NONE , ui_x + 229 , ui_y + 140 , 110 , 20 , EXPORT_COPY_IMAGES . val , ' Copy image files to the export directory, never overwrite. ' )
Draw . EndAlign ( )
Draw . Label ( ' Export... ' , ui_x + 9 , ui_y + 109 , 220 , 20 )
Draw . BeginAlign ( )
EXPORT_EDGES = Draw . Toggle ( ' Edges ' , EVENT_NONE , ui_x + 9 , ui_y + 90 , 50 , 20 , EXPORT_EDGES . val , ' Edges not connected to faces. ' )
EXPORT_TRI = Draw . Toggle ( ' Triangulate ' , EVENT_NONE , ui_x + 59 , ui_y + 90 , 70 , 20 , EXPORT_TRI . val , ' Triangulate quads. ' )
Draw . EndAlign ( )
Draw . BeginAlign ( )
EXPORT_MTL = Draw . Toggle ( ' Materials ' , EVENT_NONE , ui_x + 139 , ui_y + 90 , 70 , 20 , EXPORT_MTL . val , ' Write a separate MTL file with the OBJ. ' )
EXPORT_UV = Draw . Toggle ( ' UVs ' , EVENT_NONE , ui_x + 209 , ui_y + 90 , 31 , 20 , EXPORT_UV . val , ' Export texface UV coords. ' )
Draw . EndAlign ( )
Draw . BeginAlign ( )
EXPORT_NORMALS = Draw . Toggle ( ' Normals ' , EVENT_NONE , ui_x + 250 , ui_y + 90 , 59 , 20 , EXPORT_NORMALS . val , ' Export vertex normal data (Ignored on import). ' )
EXPORT_NORMALS_HQ = Draw . Toggle ( ' HQ ' , EVENT_NONE , ui_x + 309 , ui_y + 90 , 31 , 20 , EXPORT_NORMALS_HQ . val , ' Calculate high quality normals for rendering. ' )
Draw . EndAlign ( )
Draw . Label ( ' Blender Objects as OBJ: ' , ui_x + 9 , ui_y + 59 , 220 , 20 )
Draw . BeginAlign ( )
2008-10-21 00:21:02 +00:00
EXPORT_BLEN_OBS = Draw . Toggle ( ' Objects ' , EVENT_REDRAW , ui_x + 9 , ui_y + 39 , 60 , 20 , EXPORT_BLEN_OBS . val , ' Export blender objects as " OBJ objects " . ' , do_split )
2008-10-19 15:53:22 +00:00
EXPORT_GROUP_BY_OB = Draw . Toggle ( ' Groups ' , EVENT_REDRAW , ui_x + 69 , ui_y + 39 , 60 , 20 , EXPORT_GROUP_BY_OB . val , ' Export blender objects as " OBJ Groups " . ' , do_split )
EXPORT_GROUP_BY_MAT = Draw . Toggle ( ' Material Groups ' , EVENT_REDRAW , ui_x + 129 , ui_y + 39 , 100 , 20 , EXPORT_GROUP_BY_MAT . val , ' Group by materials. ' , do_split )
Draw . EndAlign ( )
EXPORT_KEEP_VERT_ORDER = Draw . Toggle ( ' Keep Vert Order ' , EVENT_REDRAW , ui_x + 239 , ui_y + 39 , 100 , 20 , EXPORT_KEEP_VERT_ORDER . val , ' Keep vert and face order, disables some other options. Use for morph targets. ' , do_vertorder )
Draw . BeginAlign ( )
Draw . PushButton ( ' Online Help ' , EVENT_REDRAW , ui_x + 9 , ui_y + 9 , 110 , 20 , ' Load the wiki page for this script ' , do_help )
Draw . PushButton ( ' Cancel ' , EVENT_EXIT , ui_x + 119 , ui_y + 9 , 110 , 20 , ' ' , obj_ui_set_event )
Draw . PushButton ( ' Export ' , EVENT_EXPORT , ui_x + 229 , ui_y + 9 , 110 , 20 , ' Export 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_EXPORT ) :
2008-10-22 08:21:43 +00:00
Draw . UIBlock ( obj_ui , 0 )
2008-10-19 15:53:22 +00:00
if GLOBALS [ ' EVENT ' ] != EVENT_EXPORT :
return
# END ALTERNATIVE UI *********************
2006-01-29 19:17:53 +00:00
2008-09-17 04:07:58 +00:00
if EXPORT_KEEP_VERT_ORDER . val :
2007-01-26 07:01:30 +00:00
EXPORT_BLEN_OBS . val = False
EXPORT_GROUP_BY_OB . val = False
EXPORT_GROUP_BY_MAT . val = False
EXPORT_APPLY_MODIFIERS . val = False
2006-09-25 05:12:37 +00:00
Window . EditMode ( 0 )
2006-01-29 19:17:53 +00:00
Window . WaitCursor ( 1 )
EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS . val
2006-08-06 11:32:12 +00:00
EXPORT_ROTX90 = EXPORT_ROTX90 . val
2006-01-29 19:17:53 +00:00
EXPORT_TRI = EXPORT_TRI . val
EXPORT_EDGES = EXPORT_EDGES . val
EXPORT_NORMALS = EXPORT_NORMALS . val
2006-07-06 12:25:04 +00:00
EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ . val
2006-01-29 19:17:53 +00:00
EXPORT_UV = EXPORT_UV . val
EXPORT_MTL = EXPORT_MTL . val
EXPORT_SEL_ONLY = EXPORT_SEL_ONLY . val
EXPORT_ALL_SCENES = EXPORT_ALL_SCENES . val
EXPORT_ANIMATION = EXPORT_ANIMATION . val
EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES . val
EXPORT_BLEN_OBS = EXPORT_BLEN_OBS . val
EXPORT_GROUP_BY_OB = EXPORT_GROUP_BY_OB . val
EXPORT_GROUP_BY_MAT = EXPORT_GROUP_BY_MAT . val
2008-09-17 04:07:58 +00:00
EXPORT_KEEP_VERT_ORDER = EXPORT_KEEP_VERT_ORDER . val
2006-01-29 19:17:53 +00:00
base_name , ext = splitExt ( filename )
context_name = [ base_name , ' ' , ' ' , ext ] # basename, scene_name, framenumber, extension
# Use the options to export the data using write()
# def write(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
2007-04-19 20:58:09 +00:00
context_name [ 1 ] = ' _ %s ' % BPySys . cleanName ( scn . name ) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
2006-01-29 19:17:53 +00:00
# Export an animation?
if EXPORT_ANIMATION :
2006-09-25 05:12:37 +00:00
scene_frames = xrange ( context . startFrame ( ) , context . endFrame ( ) + 1 ) # up to and including the end frame.
2006-01-29 19:17:53 +00:00
else :
scene_frames = [ orig_frame ] # Dont export an animation.
# Loop through all frames in the scene and export.
for frame in scene_frames :
if EXPORT_ANIMATION : # Add frame to the filename.
context_name [ 2 ] = ' _ %.6d ' % frame
Blender . Set ( ' curframe ' , frame )
if EXPORT_SEL_ONLY :
2006-12-15 22:14:33 +00:00
export_objects = scn . objects . context
2006-01-29 19:17:53 +00:00
else :
2006-12-25 09:17:23 +00:00
export_objects = scn . objects
2006-09-25 05:12:37 +00:00
full_path = ' ' . join ( context_name )
2007-01-26 06:02:21 +00:00
2007-10-21 17:38:17 +00:00
# erm... bit of a problem here, this can overwrite files when exporting frames. not too bad.
# EXPORT THE FILE.
write ( full_path , export_objects , \
EXPORT_TRI , EXPORT_EDGES , EXPORT_NORMALS , \
EXPORT_NORMALS_HQ , EXPORT_UV , EXPORT_MTL , \
EXPORT_COPY_IMAGES , EXPORT_APPLY_MODIFIERS , \
EXPORT_ROTX90 , EXPORT_BLEN_OBS , \
2008-09-17 04:07:58 +00:00
EXPORT_GROUP_BY_OB , EXPORT_GROUP_BY_MAT , EXPORT_KEEP_VERT_ORDER )
2006-01-29 19:17:53 +00:00
Blender . Set ( ' curframe ' , orig_frame )
# Restore old active scene.
orig_scene . makeCurrent ( )
Window . WaitCursor ( 0 )
2005-10-11 02:32:58 +00:00
2006-01-29 19:17:53 +00:00
if __name__ == ' __main__ ' :
Window . FileSelector ( write_ui , ' Export Wavefront OBJ ' , sys . makename ( ext = ' .obj ' ) )