temporarypython001
almost final version of the code:
# mcjObjSeqRender.py
# by mCasualJacques
# released under whichever permissive license is compatible with Blender's license
import bpy, math
import struct
from bpy.props import StringProperty
import os
import io_scene_obj.import_obj
from bpy_extras.io_utils import axis_conversion
import re
import string
#-------------------------------------------------------------------------------
#note line_value is derived from from io_scene_obj
def line_value(line_split):
'''
Returns 1 string represneting the value for this line
None will be returned if theres only 1 word
'''
length = len(line_split)
if length == 1:
return None
elif length == 2:
return line_split[1]
elif length > 2:
return ' '.join(line_split[1:])
#-------------------------------------------------------------------------------
# Begin Section - fix paths in mtl files - by mCasualJacques
#---------- getMtlFromObj ----------
def getMtlFromObj( filepath ):
filepath = os.fsencode( filepath )
file = open( filepath, 'r' )
for line in file:
line = line.lstrip()
if line.startswith('mtllib'):
file.close()
l = len( line )
return( line[7:l-1] )
file.close()
return( 0 )
#-------------- outMap -----------------
# line is a of the form : map-filePath
# if first char of the filePath is '/' then this is a relative path in a format
# that is not handled well by the importer so we remove the leading '/'
def outMap( out, line, key ):
img_filepath = line_value(line.split())
if img_filepath:
if img_filepath[0] == '/':
out.write( key + ' ' + img_filepath[1:] + '\n' )
else:
out.write( key + ' ' + img_filepath + '\n' )
#-------------- mapNeedsFix -----------------
# line is a of the form : map-filePath
# if first char of the filePath is '/' then this is a relative path in a format
# that is not handled well by the importer so we remove the leading '/'
def mapNeedsFix( line, key ):
img_filepath = line_value(line.split())
if img_filepath:
if img_filepath[0] == '/':
return( True )
else:
return( False )
#---------- mtlNeedsFix ----------
def mtlNeedsFix(filepath,mtllib):
DIR = os.path.dirname(filepath)
mtlpath = os.path.join( DIR, mtllib )
if not os.path.exists(mtlpath):
return( False )
mtl = open(mtlpath, 'r')
for line in mtl:
line = line.strip()
line_lower = line.lower().lstrip()
if not line:
pass
elif line_lower.startswith('km'): #we'll remove the Kms
mtl.close()
return( True )
elif line_lower.startswith('map_ka'):
if( mapNeedsFix( line, 'map_ka' ) ):
mtl.close()
return( True )
elif line_lower.startswith('map_ks'):
if( mapNeedsFix( line, 'map_ks' ) ):
mtl.close()
return( True )
elif line_lower.startswith('map_kd'):
if( mapNeedsFix( line, 'map_kd' ) ):
mtl.close()
return( True )
elif line_lower.startswith('map_bump'):
if( mapNeedsFix( line, 'map_bump' ) ):
mtl.close()
return( True )
elif line_lower.startswith( 'bump'):
if( mapNeedsFix( line, 'bump' ) ):
mtl.close()
return( True )
elif line_lower.startswith('map_d'):
if( mapNeedsFix( line, 'map_d' ) ):
mtl.close()
return( True )
elif line_lower.startswith('map_tr'):
if( mapNeedsFix( line, 'map_tr' ) ):
mtl.close()
return( True )
elif line_lower.startswith('refl'):
if( mapNeedsFix( line, 'refl' ) ):
mtl.close()
return( True )
mtl.close()
return( False )
#---------- fixMtlPaths ----------
def fixMtlPaths( filepath ):
mtllib = getMtlFromObj( filepath );
if not mtllib:
return
if not mtlNeedsFix(filepath,mtllib):
return
print( "fixing .mtl paths" )
DIR = os.path.dirname(filepath)
mtlpath = os.path.join( DIR, mtllib )
if not os.path.exists(mtlpath):
return
mtl = open(mtlpath, 'r')
tempPath = os.path.join( DIR, 'temp.txt' );
out = open( tempPath , 'w')
out.write( '# Leading front slashes in map paths were removed' + '\n' )
for line in mtl:
line = line.strip()
line_lower = line.lower().lstrip()
if not line:
pass
if line.startswith('#'):
out.write( line + '\n' )
elif line_lower.startswith('km'):
#the importer reports Km as errors
out.write( '#' + line + '\n' )
elif line_lower.startswith('map_ka'):
outMap( out, line, 'map_ka' )
elif line_lower.startswith('map_ks'):
outMap( out, line, 'map_ks' )
elif line_lower.startswith('map_kd'):
outMap( out, line, 'map_kd' )
elif line_lower.startswith('map_bump'):
outMap( out, line, 'map_bump' )
elif line_lower.startswith( 'bump'):
outMap( out, line, 'bump' )
elif line_lower.startswith('map_d'):
outMap( out, line, 'map_d' )
elif line_lower.startswith('map_tr'):
outMap( out, line, 'map_tr' )
elif line_lower.startswith('refl'):
outMap( out, line, 'refl' )
else:
out.write( line + '\n' )
mtl.close()
out.close()
os.remove( mtlpath )
os.renames( tempPath, mtlpath );
# End Section - fix mtl paths by mCasualJacques
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Begin Section - by mCasualJacques
# switch materials of newly loaded objects
# to the materials with the same root-names
# those materials were created/modified when
# the first .obj in the sequence of .objs was loaded
#------------- findMaterial -----------------
def findMaterial(name):
allmats = bpy.data.materials
for material in allmats:
# material root-name truncation may
# occur during obj-import
# ex: a material named 'antidisestablishmen'
# the first time the obj-importer encounters it
# it will be stored as 'antidisestablishmen'
# the second time the obj-importer encounters it
# it will be stored as 'antidisestablishm.001'
n = len( name )
oldname = material.name
leno = len( oldname )
if( leno >= n ):
if oldname[0:n] == name:
if( leno == n ):
return material
else:
if( oldname[n] != '.' ):
return material
#--------------- switchMatsToBase ------------
def switchMatsToBase( obj_names ):
for obj_name in obj_names:
obj = bpy.data.objects[obj_name]
for matslot in obj.material_slots:
mat = matslot.material
( root, ext ) = os.path.splitext( mat.name )
if( len( ext ) > 0 ):
baseMap = findMaterial( root )
if baseMap:
matslot.material = baseMap
# end Section - by mCasualJacques
#-------------------------------------------------------------------------------
# Begin Section : mesh loader-renderer-unloader code by TomHarding
# http://blenderartists.org/forum/archive/index.php/t-228040.html?s=3fbeeee6aedf70853b58826dcf82b5e1
def meshStreamer( frameNo, objPath, outPath, bMats, bFixMapPaths, bSimulate ):
print( 'frame: %d'%frameNo + ', in: ' + objPath + ', out: ' + outPath )
if( bSimulate ) :
return
if bFixMapPaths:
fixMtlPaths( objPath )
#get list of current objects and meshes
before_obj_list = []
before_mesh_list = []
for object in bpy.data.objects:
before_obj_list.append(object.name)
for mesh in bpy.data.meshes:
before_mesh_list.append(mesh.name)
#load the appropriate obj file
gm = axis_conversion( from_forward='-Z', from_up='Y' ).to_4x4()
io_scene_obj.import_obj.load(bpy.ops,bpy.context,objPath,global_matrix=gm)
#get new list of objects and meshes
after_obj_list = []
after_mesh_list = []
for object in bpy.data.objects:
after_obj_list.append(object.name)
for mesh in bpy.data.meshes:
after_mesh_list.append(mesh.name)
#get new object & mesh names
new_obj_names = [item for item in after_obj_list if not item in before_obj_list]
new_mesh_names = [item for item in after_mesh_list if not item in before_mesh_list]
# re-use materials from the first .obj
if bMats:
switchMatsToBase( new_obj_names )
#set render current frame
bpy.context.scene.frame_set( frameNo )
bpy.context.scene.render.filepath = outPath
bpy.ops.render.render( animation = False, write_still =True, layer="", scene="" )
#remove added data from project & memory
for name in new_obj_names:
ob = bpy.data.objects[name]
bpy.context.scene.objects.unlink(ob)
bpy.data.objects.remove(ob)
for name in new_mesh_names:
mesh = bpy.data.meshes[name]
bpy.data.meshes.remove(mesh)
# End Section : mesh loader-renderer-unloader code by TomHarding
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# begin numbered file list building section
# derived from code by
# Chip Chapin, possibly Luc-Eric Rousseau, and John Cromie
def seq_file_list( a_seq_file ):
(work_folder, sequence_proto) = os.path.split(a_seq_file)
sm = re.match("(.*?)[0-9]+\.", sequence_proto)
sq_name = sequence_proto
if not sm:
sorted_sq_files = [[a_seq_file, '-1']]
( sq_name, ext ) = os.path.splitext( sequence_proto )
return( sorted_sq_files, sq_name, work_folder )
sq_name = sm.group(1)
sq_re = re.compile("^" + sq_name + "[0-9]+\." );
sq_files = []
dir = os.listdir(work_folder)
chopoff = len( sq_name )
for f in dir:
if sq_re.match(f):
ext = os.path.splitext( f )[1]
if( ext == ".obj" ):
numandext = f[chopoff:]
(num, ext) = os.path.splitext(numandext)
sq_files.append([f,'%06d'% int(num)]) #zero-padded for sorting
sorted_sq_files = sorted(sq_files, key=lambda col: col[1])
return( sorted_sq_files, sq_name, work_folder )
#end numbered file list building section
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Begin Section : render sequence of numbered objs, code by mCasual/Jacques
#-------------------- doit --------------------
def doit( filepath, bMats, bFixMapPaths, fObjStart, fObjStep, bSimulate ):
print( "\n------- mcjObjSeqRender begins -------\n")
scene = bpy.context.scene
render = scene.render
ext = render.image_settings.file_format
( sq_files, sq_name, work_folder ) = seq_file_list( filepath )
sq_count = len( sq_files )
frame_start = bpy.context.scene.frame_start
frame_end = bpy.context.scene.frame_end
frame_step = bpy.context.scene.frame_step
sep = '\\'
numObj = len( sq_files )
if fObjStart < 0 :
fObjStart = 0
if fObjStart >= numObj:
fObjStart = numObj - 1
idxObj = fObjStart
if( sq_files[0][1] == '-1' ): #not a numbered file
objPath = sq_files[0][0]
outPath = work_folder + sep + sq_name + '.' + ext
meshStreamer( frame_start, objPath, outPath, bMats, bFixMapPaths, bSimulate )
return {'FINISHED'}
for frame in range( frame_start, frame_end + 1, frame_step ):
objPath = work_folder + sep + sq_files[idxObj][0]
outPath = work_folder + sep + sq_name + '%d'%frame + '.' + ext
meshStreamer( frame, objPath, outPath, bMats, bFixMapPaths, bSimulate )
idxObj = ( idxObj + fObjStep ) % numObj
return {'FINISHED'}
# End Section : mCasual/Jacques
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Begin Section : file selection code derived from code by Jacob Valenta
# http://blenderapi.wordpress.com/2011/09/26/file-selection-with-python/
class McjBatchObjRenderer(bpy.types.Operator):
bl_idname = "object.mcj_batch_obj_render"
bl_label = "Import"
filename_ext = ".obj"
filter_glob = StringProperty(default="*.obj", options={'HIDDEN'})
filepath = StringProperty(subtype="FILE_PATH")
bMats = bpy.props.BoolProperty(name="Re-Use initial materials")
bFixMapPaths = bpy.props.BoolProperty(name = "Fix .mtl files")
bSimulate = bpy.props.BoolProperty(name="simulate/log")
fObjStart = bpy.props.IntProperty(name=".obj start index [0...]")
fObjStep = bpy.props.IntProperty(name=".obj step")
def execute(self, context):
doit( self.filepath, self.bMats, self.bFixMapPaths, self.fObjStart, self.fObjStep, self.bSimulate )
return {'FINISHED'}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
self.bMats = True
self.bFixMapPaths = True
self.fObjStart = 0
self.fObjStep = 1
self.bSimulate = False
return {'RUNNING_MODAL'}
def draw(self, context):
layout = self.layout
col = layout.column()
col.label(text="Warning: By pressing Ctrl-C in the System" )
col.label(text="Console, you will be able to interrupt this" )
col.label(text="script, otherwise your only option will be" )
col.label(text="to close Blender. You can open the console" )
col.label(text="from Blender's Help menu. You can also" )
col.label(text="monitor the render progress in the console." )
row = col.row()
col.prop(self, "bMats" )
col.prop(self, "bFixMapPaths" )
col.prop(self, "fObjStart" )
col.prop(self, "fObjStep" )
col.prop(self, "bSimulate" )
bpy.utils.register_class(McjBatchObjRenderer)
bpy.ops.object.mcj_batch_obj_render('INVOKE_DEFAULT')
# End Section : file selection code by Jacob Valenta
#-------------------------------------------------------------------------------