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
#-------------------------------------------------------------------------------