Commits

Richard Plangger committed 50f8b75

refactoring on materials and skeleton
added api functions (api.py)
dot_mesh -> only exports the mesh
dot_material -> only exports the material file
dot_skeleton -> only exports the skeleton

Comments (0)

Files changed (10)

+from .ogre.mesh import dot_mesh
+from .ogre.skeleton import dot_skeleton
+from .ogre.material import dot_material
+
+# just import various functions that can be used to export objects
+

io_ogre/ogre/export.py

 from bpy.props import EnumProperty, BoolProperty, FloatProperty, StringProperty, IntProperty
 from .material import *
 from .. import config
-from ..config import CONFIG
 from ..report import Report
 from ..util import *
 from ..xml import *
 from .mesh import *
+from . import mesh
+from . import skeleton
 
 def export_mesh(ob, path='/tmp', force_name=None, ignore_shape_animation=False, normals=True):
     ''' returns materials used by the mesh '''
         return {'FINISHED'}
 
     def update_ui(self):
-        self.EX_SWAP_AXIS = CONFIG['SWAP_AXIS']
-        self.EX_SEP_MATS = CONFIG['SEP_MATS']
-        self.EX_ONLY_DEFORMABLE_BONES = CONFIG['ONLY_DEFORMABLE_BONES']
-        self.EX_ONLY_KEYFRAMED_BONES = CONFIG['ONLY_KEYFRAMED_BONES']
-        self.EX_OGRE_INHERIT_SCALE = CONFIG['OGRE_INHERIT_SCALE']
-        self.EX_SCENE = CONFIG['SCENE']
-        self.EX_EXPORT_HIDDEN = CONFIG['EXPORT_HIDDEN']
-        self.EX_SELONLY = CONFIG['SELONLY']
-        self.EX_FORCE_CAMERA = CONFIG['FORCE_CAMERA']
-        self.EX_FORCE_LAMPS = CONFIG['FORCE_LAMPS']
-        self.EX_MESH = CONFIG['MESH']
-        self.EX_MESH_OVERWRITE = CONFIG['MESH_OVERWRITE']
-        self.EX_ARM_ANIM = CONFIG['ARM_ANIM']
-        self.EX_SHAPE_ANIM = CONFIG['SHAPE_ANIM']
-        self.EX_TRIM_BONE_WEIGHTS = CONFIG['TRIM_BONE_WEIGHTS']
-        self.EX_ARRAY = CONFIG['ARRAY']
-        self.EX_MATERIALS = CONFIG['MATERIALS']
-        self.EX_FORCE_IMAGE_FORMAT = CONFIG['FORCE_IMAGE_FORMAT']
-        self.EX_DDS_MIPS = CONFIG['DDS_MIPS']
-        self.EX_COPY_SHADER_PROGRAMS = CONFIG['COPY_SHADER_PROGRAMS']
-        self.EX_lodLevels = CONFIG['lodLevels']
-        self.EX_lodDistance = CONFIG['lodDistance']
-        self.EX_lodPercent = CONFIG['lodPercent']
-        self.EX_nuextremityPoints = CONFIG['nuextremityPoints']
-        self.EX_generateEdgeLists = CONFIG['generateEdgeLists']
-        self.EX_generateTangents = CONFIG['generateTangents']
-        self.EX_tangentSemantic = CONFIG['tangentSemantic']
-        self.EX_tangentUseParity = CONFIG['tangentUseParity']
-        self.EX_tangentSplitMirrored = CONFIG['tangentSplitMirrored']
-        self.EX_tangentSplitRotated = CONFIG['tangentSplitRotated']
-        self.EX_reorganiseBuffers = CONFIG['reorganiseBuffers']
-        self.EX_optimiseAnimations = CONFIG['optimiseAnimations']
+        self.EX_SWAP_AXIS = config.get('SWAP_AXIS')
+        self.EX_SEP_MATS = config.get('SEP_MATS')
+        self.EX_ONLY_DEFORMABLE_BONES = config.get('ONLY_DEFORMABLE_BONES')
+        self.EX_ONLY_KEYFRAMED_BONES = config.get('ONLY_KEYFRAMED_BONES')
+        self.EX_OGRE_INHERIT_SCALE = config.get('OGRE_INHERIT_SCALE')
+        self.EX_SCENE = config.get('SCENE')
+        self.EX_EXPORT_HIDDEN = config.get('EXPORT_HIDDEN')
+        self.EX_SELONLY = config.get('SELONLY')
+        self.EX_FORCE_CAMERA = config.get('FORCE_CAMERA')
+        self.EX_FORCE_LAMPS = config.get('FORCE_LAMPS')
+        self.EX_MESH = config.get('MESH')
+        self.EX_MESH_OVERWRITE = config.get('MESH_OVERWRITE')
+        self.EX_ARM_ANIM = config.get('ARM_ANIM')
+        self.EX_SHAPE_ANIM = config.get('SHAPE_ANIM')
+        self.EX_TRIM_BONE_WEIGHTS = config.get('TRIM_BONE_WEIGHTS')
+        self.EX_ARRAY = config.get('ARRAY')
+        self.EX_MATERIALS = config.get('MATERIALS')
+        self.EX_FORCE_IMAGE_FORMAT = config.get('FORCE_IMAGE_FORMAT')
+        self.EX_DDS_MIPS = config.get('DDS_MIPS')
+        self.EX_COPY_SHADER_PROGRAMS = config.get('COPY_SHADER_PROGRAMS')
+        self.EX_lodLevels = config.get('lodLevels')
+        self.EX_lodDistance = config.get('lodDistance')
+        self.EX_lodPercent = config.get('lodPercent')
+        self.EX_nuextremityPoints = config.get('nuextremityPoints')
+        self.EX_generateEdgeLists = config.get('generateEdgeLists')
+        self.EX_generateTangents = config.get('generateTangents')
+        self.EX_tangentSemantic = config.get('tangentSemantic')
+        self.EX_tangentUseParity = config.get('tangentUseParity')
+        self.EX_tangentSplitMirrored = config.get('tangentSplitMirrored')
+        self.EX_tangentSplitRotated = config.get('tangentSplitRotated')
+        self.EX_reorganiseBuffers = config.get('reorganiseBuffers')
+        self.EX_optimiseAnimations = config.get('optimiseAnimations')
 
     # Basic options
     EX_SWAP_AXIS = EnumProperty(
         items=config.AXIS_MODES,
         name='swap axis',
         description='axis swapping mode',
-        default= CONFIG['SWAP_AXIS'])
+        default= config.get('SWAP_AXIS'))
     EX_SEP_MATS = BoolProperty(
         name="Separate Materials",
         description="exports a .material for each material (rather than putting all materials in a single .material file)",
-        default=CONFIG['SEP_MATS'])
+        default=config.get('SEP_MATS'))
     EX_ONLY_DEFORMABLE_BONES = BoolProperty(
         name="Only Deformable Bones",
         description="only exports bones that are deformable. Useful for hiding IK-Bones used in Blender. Note: Any bone with deformable children/descendants will be output as well.",
-        default=CONFIG['ONLY_DEFORMABLE_BONES'])
+        default=config.get('ONLY_DEFORMABLE_BONES'))
     EX_ONLY_KEYFRAMED_BONES = BoolProperty(
         name="Only Keyframed Bones",
         description="only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis.",
-        default=CONFIG['ONLY_KEYFRAMED_BONES'])
+        default=config.get('ONLY_KEYFRAMED_BONES'))
     EX_OGRE_INHERIT_SCALE = BoolProperty(
         name="OGRE inherit scale",
         description="whether the OGRE bones have the 'inherit scale' flag on.  If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE.",
-        default=CONFIG['OGRE_INHERIT_SCALE'])
+        default=config.get('OGRE_INHERIT_SCALE'))
     EX_SCENE = BoolProperty(
         name="Export Scene",
         description="export current scene (OgreDotScene xml)",
-        default=CONFIG['SCENE'])
+        default=config.get('SCENE'))
     EX_SELONLY = BoolProperty(
         name="Export Selected Only",
         description="export selected",
-        default=CONFIG['SELONLY'])
+        default=config.get('SELONLY'))
     EX_EXPORT_HIDDEN = BoolProperty(
         name="Export Hidden Also",
         description="Export hidden meshes in addition to visible ones. Turn off to avoid exporting hidden stuff.",
-        default=CONFIG['EXPORT_HIDDEN'])
+        default=config.get('EXPORT_HIDDEN'))
     EX_FORCE_CAMERA = BoolProperty(
         name="Force Camera",
         description="export active camera",
-        default=CONFIG['FORCE_CAMERA'])
+        default=config.get('FORCE_CAMERA'))
     EX_FORCE_LAMPS = BoolProperty(
         name="Force Lamps",
         description="export all lamps",
-        default=CONFIG['FORCE_LAMPS'])
+        default=config.get('FORCE_LAMPS'))
     EX_MESH = BoolProperty(
         name="Export Meshes",
         description="export meshes",
-        default=CONFIG['MESH'])
+        default=config.get('MESH'))
     EX_MESH_OVERWRITE = BoolProperty(
         name="Export Meshes (overwrite)",
         description="export meshes (overwrite existing files)",
-        default=CONFIG['MESH_OVERWRITE'])
+        default=config.get('MESH_OVERWRITE'))
     EX_ARM_ANIM = BoolProperty(
         name="Armature Animation",
         description="export armature animations - updates the .skeleton file",
-        default=CONFIG['ARM_ANIM'])
+        default=config.get('ARM_ANIM'))
     EX_SHAPE_ANIM = BoolProperty(
         name="Shape Animation",
         description="export shape animations - updates the .mesh file",
-        default=CONFIG['SHAPE_ANIM'])
+        default=config.get('SHAPE_ANIM'))
     EX_TRIM_BONE_WEIGHTS = FloatProperty(
         name="Trim Weights",
         description="ignore bone weights below this value (Ogre supports 4 bones per vertex)",
-        min=0.0, max=0.5, default=CONFIG['TRIM_BONE_WEIGHTS'] )
+        min=0.0, max=0.5, default=config.get('TRIM_BONE_WEIGHTS') )
     EX_ARRAY = BoolProperty(
         name="Optimize Arrays",
         description="optimize array modifiers as instances (constant offset only)",
-        default=CONFIG['ARRAY'])
+        default=config.get('ARRAY'))
     EX_MATERIALS = BoolProperty(
         name="Export Materials",
         description="exports .material script",
-        default=CONFIG['MATERIALS'])
+        default=config.get('MATERIALS'))
     EX_DDS_MIPS = IntProperty(
         name="DDS Mips",
         description="number of mip maps (DDS)",
         min=0, max=16,
-        default=CONFIG['DDS_MIPS'])
+        default=config.get('DDS_MIPS'))
 
     # Mesh options
     EX_lodLevels = IntProperty(
         name="LOD Levels",
         description="MESH number of LOD levels",
         min=0, max=32,
-        default=CONFIG['lodLevels'])
+        default=config.get('lodLevels'))
     EX_lodDistance = IntProperty(
         name="LOD Distance",
         description="MESH distance increment to reduce LOD",
-        min=0, max=2000, default=CONFIG['lodDistance'])
+        min=0, max=2000, default=config.get('lodDistance'))
     EX_lodPercent = IntProperty(
         name="LOD Percentage",
         description="LOD percentage reduction",
         min=0, max=99,
-        default=CONFIG['lodPercent'])
+        default=config.get('lodPercent'))
     EX_nuextremityPoints = IntProperty(
         name="Extremity Points",
         description="MESH Extremity Points",
         min=0, max=65536,
-        default=CONFIG['nuextremityPoints'])
+        default=config.get('nuextremityPoints'))
     EX_generateEdgeLists = BoolProperty(
         name="Edge Lists",
         description="MESH generate edge lists (for stencil shadows)",
-        default=CONFIG['generateEdgeLists'])
+        default=config.get('generateEdgeLists'))
     EX_generateTangents = BoolProperty(
         name="Tangents",
         description="MESH generate tangents",
-        default=CONFIG['generateTangents'])
+        default=config.get('generateTangents'))
     EX_tangentSemantic = StringProperty(
         name="Tangent Semantic",
         description="MESH tangent semantic - can be 'uvw' or 'tangent'",
         maxlen=16,
-        default=CONFIG['tangentSemantic'])
+        default=config.get('tangentSemantic'))
     EX_tangentUseParity = IntProperty(
         name="Tangent Parity",
         description="MESH tangent use parity",
         min=0, max=16,
-        default=CONFIG['tangentUseParity'])
+        default=config.get('tangentUseParity'))
     EX_tangentSplitMirrored = BoolProperty(
         name="Tangent Split Mirrored",
         description="MESH split mirrored tangents",
-        default=CONFIG['tangentSplitMirrored'])
+        default=config.get('tangentSplitMirrored'))
     EX_tangentSplitRotated = BoolProperty(
         name="Tangent Split Rotated",
         description="MESH split rotated tangents",
-        default=CONFIG['tangentSplitRotated'])
+        default=config.get('tangentSplitRotated'))
     EX_reorganiseBuffers = BoolProperty(
         name="Reorganise Buffers",
         description="MESH reorganise vertex buffers",
-        default=CONFIG['reorganiseBuffers'])
+        default=config.get('reorganiseBuffers'))
     EX_optimiseAnimations = BoolProperty(
         name="Optimize Animations",
         description="MESH optimize animations",
-        default=CONFIG['optimiseAnimations'])
+        default=config.get('optimiseAnimations'))
     EX_COPY_SHADER_PROGRAMS = BoolProperty(
         name="copy shader programs",
         description="when using script inheritance copy the source shader programs to the output path",
-        default=CONFIG['COPY_SHADER_PROGRAMS'])
+        default=config.get('COPY_SHADER_PROGRAMS'))
 
     filepath_last = ""
     filepath = StringProperty(
     EX_SEP_MATS = BoolProperty(
         name="Separate Materials",
         description="exports a .material for each material (rather than putting all materials in a single .material file)",
-        default=CONFIG['SEP_MATS'])
+        default=config.get('SEP_MATS'))
     EX_ONLY_DEFORMABLE_BONES = BoolProperty(
         name="Only Deformable Bones",
         description="only exports bones that are deformable. Useful for hiding IK-Bones used in Blender. Note: Any bone with deformable children/descendants will be output as well.",
-        default=CONFIG['ONLY_DEFORMABLE_BONES'])
+        default=config.get('ONLY_DEFORMABLE_BONES'))
     EX_ONLY_KEYFRAMED_BONES = BoolProperty(
         name="Only Keyframed Bones",
         description="only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis.",
-        default=CONFIG['ONLY_KEYFRAMED_BONES'])
+        default=config.get('ONLY_KEYFRAMED_BONES'))
     EX_OGRE_INHERIT_SCALE = BoolProperty(
         name="OGRE inherit scale",
         description="whether the OGRE bones have the 'inherit scale' flag on.  If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE.",
-        default=CONFIG['OGRE_INHERIT_SCALE'])
+        default=config.get('OGRE_INHERIT_SCALE'))
     EX_SCENE = BoolProperty(
         name="Export Scene",
         description="export current scene (OgreDotScene xml)",
-        default=CONFIG['SCENE'])
+        default=config.get('SCENE'))
     EX_SELONLY = BoolProperty(
         name="Export Selected Only",
         description="export selected",
-        default=CONFIG['SELONLY'])
+        default=config.get('SELONLY'))
     EX_EXPORT_HIDDEN = BoolProperty(
         name="Export Hidden Also",
         description="Export hidden meshes in addition to visible ones. Turn off to avoid exporting hidden stuff.",
-        default=CONFIG['EXPORT_HIDDEN'])
+        default=config.get('EXPORT_HIDDEN'))
     EX_FORCE_CAMERA = BoolProperty(
         name="Force Camera",
         description="export active camera",
-        default=CONFIG['FORCE_CAMERA'])
+        default=config.get('FORCE_CAMERA'))
     EX_FORCE_LAMPS = BoolProperty(
         name="Force Lamps",
         description="export all lamps",
-        default=CONFIG['FORCE_LAMPS'])
+        default=config.get('FORCE_LAMPS'))
     EX_MESH = BoolProperty(
         name="Export Meshes",
         description="export meshes",
-        default=CONFIG['MESH'])
+        default=config.get('MESH'))
     EX_MESH_OVERWRITE = BoolProperty(
         name="Export Meshes (overwrite)",
         description="export meshes (overwrite existing files)",
-        default=CONFIG['MESH_OVERWRITE'])
+        default=config.get('MESH_OVERWRITE'))
     EX_ARM_ANIM = BoolProperty(
         name="Armature Animation",
         description="export armature animations - updates the .skeleton file",
-        default=CONFIG['ARM_ANIM'])
+        default=config.get('ARM_ANIM'))
     EX_SHAPE_ANIM = BoolProperty(
         name="Shape Animation",
         description="export shape animations - updates the .mesh file",
-        default=CONFIG['SHAPE_ANIM'])
+        default=config.get('SHAPE_ANIM'))
     EX_TRIM_BONE_WEIGHTS = FloatProperty(
         name="Trim Weights",
         description="ignore bone weights below this value (Ogre supports 4 bones per vertex)",
-        min=0.0, max=0.5, default=CONFIG['TRIM_BONE_WEIGHTS'] )
+        min=0.0, max=0.5, default=config.get('TRIM_BONE_WEIGHTS') )
     EX_ARRAY = BoolProperty(
         name="Optimize Arrays",
         description="optimize array modifiers as instances (constant offset only)",
-        default=CONFIG['ARRAY'])
+        default=config.get('ARRAY'))
     EX_MATERIALS = BoolProperty(
         name="Export Materials",
         description="exports .material script",
-        default=CONFIG['MATERIALS'])
+        default=config.get('MATERIALS'))
     EX_FORCE_IMAGE_FORMAT = EnumProperty(
         items=IMAGE_FORMATS,
         name='Convert Images',
         description='convert all textures to format',
-        default=CONFIG['FORCE_IMAGE_FORMAT'] )
+        default=config.get('FORCE_IMAGE_FORMAT') )
     EX_DDS_MIPS = IntProperty(
         name="DDS Mips",
         description="number of mip maps (DDS)",
         min=0, max=16,
-        default=CONFIG['DDS_MIPS'])
+        default=config.get('DDS_MIPS'))
 
     # Mesh options
     EX_lodLevels = IntProperty(
         name="LOD Levels",
         description="MESH number of LOD levels",
         min=0, max=32,
-        default=CONFIG['lodLevels'])
+        default=config.get('lodLevels'))
     EX_lodDistance = IntProperty(
         name="LOD Distance",
         description="MESH distance increment to reduce LOD",
         min=0, max=2000,
-        default=CONFIG['lodDistance'])
+        default=config.get('lodDistance'))
     EX_lodPercent = IntProperty(
         name="LOD Percentage",
         description="LOD percentage reduction",
         min=0, max=99,
-        default=CONFIG['lodPercent'])
+        default=config.get('lodPercent'))
     EX_nuextremityPoints = IntProperty(
         name="Extremity Points",
         description="MESH Extremity Points",
         min=0, max=65536,
-        default=CONFIG['nuextremityPoints'])
+        default=config.get('nuextremityPoints'))
     EX_generateEdgeLists = BoolProperty(
         name="Edge Lists",
         description="MESH generate edge lists (for stencil shadows)",
-        default=CONFIG['generateEdgeLists'])
+        default=config.get('generateEdgeLists'))
     EX_generateTangents = BoolProperty(
         name="Tangents",
         description="MESH generate tangents",
-        default=CONFIG['generateTangents'])
+        default=config.get('generateTangents'))
     EX_tangentSemantic = StringProperty(
         name="Tangent Semantic",
         description="MESH tangent semantic",
         maxlen=16,
-        default=CONFIG['tangentSemantic'])
+        default=config.get('tangentSemantic'))
     EX_tangentUseParity = IntProperty(
         name="Tangent Parity",
         description="MESH tangent use parity",
         min=0, max=16,
-        default=CONFIG['tangentUseParity'])
+        default=config.get('tangentUseParity'))
     EX_tangentSplitMirrored = BoolProperty(
         name="Tangent Split Mirrored",
         description="MESH split mirrored tangents",
-        default=CONFIG['tangentSplitMirrored'])
+        default=config.get('tangentSplitMirrored'))
     EX_tangentSplitRotated = BoolProperty(
         name="Tangent Split Rotated",
         description="MESH split rotated tangents",
-        default=CONFIG['tangentSplitRotated'])
+        default=config.get('tangentSplitRotated'))
     EX_reorganiseBuffers = BoolProperty(
         name="Reorganise Buffers",
         description="MESH reorganise vertex buffers",
-        default=CONFIG['reorganiseBuffers'])
+        default=config.get('reorganiseBuffers'))
     EX_optimiseAnimations = BoolProperty(
         name="Optimize Animations",
         description="MESH optimize animations",
-        default=CONFIG['optimiseAnimations'])
+        default=config.get('optimiseAnimations'))
 
     filepath= StringProperty(
         name="File Path",
             if mat is None:
                 continue
             Report.materials.append( material_name(mat) )
-            if CONFIG['COPY_SHADER_PROGRAMS']:
-                data = generate_material( mat, path=path, copy_programs=True, touch_textures=CONFIG['TOUCH_TEXTURES'] )
+            if config.get('COPY_SHADER_PROGRAMS'):
+                data = generate_material( mat, path=path, copy_programs=True, touch_textures=config.get('TOUCH_TEXTURES') )
             else:
-                data = generate_material( mat, path=path, touch_textures=CONFIG['TOUCH_TEXTURES'] )
+                data = generate_material( mat, path=path, touch_textures=config.get('TOUCH_TEXTURES') )
 
             M += data
             # Write own .material file per material
             show_dialog("Invalid material object name: " + clean_filename)
             return ""
 
-    def dot_mesh( self, ob, path='/tmp', force_name=None, ignore_shape_animation=False ):
-        dot_mesh( ob, path, force_name, ignore_shape_animation=False )
-
     def ogre_export(self, url, context, force_material_update=[]):
         print ("_"*80)
 
+        target_path = os.path.split(url)[0]
+
         # Updating config to latest values?
-        global CONFIG
+        kw = {}
         for name in dir(self):
             if name.startswith('EX_'):
-                CONFIG[ name[3:] ] = getattr(self,name)
+                kw[ name[3:] ] = getattr(self,name)
+        config.update(**kw)
 
         Report.reset()
 
             material_files = []
 
         # realXtend Tundra .txml scene description export
-        # TODO re enable this export type
+        # TUNDRA TODO re enable this export type
         #if self.EXPORT_TYPE == 'REX':
         #    rex = self.create_tundra_document(context)
         #    proxies = []
                             break
                     if collisionFile:
                         mesh_collision_files[ ob.data.name ] = collisionFile
-                        self.dot_mesh(
-                            child,
-                            path=os.path.split(url)[0],
-                            force_name='_collision_%s' %ob.data.name
-                        )
+                        mesh.dot_mesh(child, target_path, force_name='_collision_%s' % ob.data.name )
+                        skeleton.dot_skeleton(child, target_path)
 
             if collisionPrim:
                 e.setAttribute('collisionPrim', collisionPrim )
                 if not exists or (exists and self.EX_MESH_OVERWRITE):
                     if ob.data.name not in exported_meshes:
                         exported_meshes.append( ob.data.name )
-                        self.dot_mesh( ob, os.path.split(url)[0] )
+                        mesh.dot_mesh(ob, target_path)
+                        skeleton.dot_skeleton(ob, target_path)
 
             # Deal with Array modifier
             vecs = [ ob.matrix_world.to_translation() ]

io_ogre/ogre/material.py

 from datetime import datetime
 import os
+from os.path import join
 from ..util import *
+from .. import util
 from .. import config
 from .. import shader
+from ..report import Report
+import tempfile
+import shutil
 
+def dot_material(obj, path, **kwargs):
+    """
+    obj: a blender object that has a mesh
+    path: target directory to save the file to
+
+    kwargs: 
+      * prefix - string. The prefix name of the file. default ''
+      * copy_programs - bool. default False
+      * touch_textures - bool. Copy the images along to the material files.
+    """
+    prefix = kwargs.get('prefix', '')
+    for material in obj.data.materials:
+        material_text = generate_material(material, path, **kwargs)
+        mat_name = material_name(material, prefix=prefix) + '.material'
+        with open(join(path, mat_name), 'wb') as fd:
+            fd.write(bytes(material_text,'utf-8'))
+        yield mat_name
+
+    if kwargs.get('copy_materials', False):
+        _copy_materials(obj.data.materials, path)
+
+def _copy_materials(materials, path):
+    pass
 # Make default material for missing materials:
 # * Red flags for users so they can quickly see what they forgot to assign a material to.
 # * Do not crash if no material on object - thats annoying for the user.
         scripts,progs = update_parent_material_path( config.get('USER_MATERIALS') )
         for prog in progs:
             logging.info('Ogre shader program', prog.name)
-    else:
-        logging.warn('Invalid my-shaders path %s' % config.get('USER_MATERIALS'))
+    #else:
+    #    logging.warn('Invalid my-shaders path %s' % config.get('USER_MATERIALS'))
 
 
-def material_name( mat, clean = False ):
+def material_name( mat, clean = False, prefix='' ):
     if type(mat) is str:
-        return mat
+        return prefix + mat
     elif not mat.library:
-        return mat.name
+        return prefix + mat.name
     else:
         if clean:
-            return clean_object_name(mat.name + mat.library.filepath.replace('/','_'))
+            return prefix + clean_object_name(mat.name + mat.library.filepath.replace('/','_'))
+        else:
+            return prefix + clean_object_name(mat.name)
 
-def generate_material(mat, path='/tmp', copy_programs=False, touch_textures=False):
+def generate_material(mat, path='/tmp', copy_programs=False, touch_textures=False, **kwargs):
     ''' returns generated material string '''
 
-    safename = material_name(mat) # supports blender library linking
-    M = '// %s generated by blender2ogre %s\n\n' % (mat.name, datetime.now())
+    prefix = kwargs.get('prefix','')
+    safename = material_name(mat,prefix=prefix) # supports blender library linking
+    w = util.IndentedWriter()
+    w.line('// %s generated by blender2ogre %s' % (mat.name, datetime.now())).nl()
 
-    M += 'material %s \n{\n' % safename # start material
-    if mat.use_shadows:
-        M += indent(1, 'receive_shadows on \n')
-    else:
-        M += indent(1, 'receive_shadows off \n')
+    with w.iword('material').word(safename).embed():
+        if mat.use_shadows:
+            w.iline('receive_shadows on')
+        else:
+            w.iline('receive_shadows off')
 
-    M += indent(1, 'technique', '{' ) # technique GLSL, CG
-    w = OgreMaterialGenerator(mat, path=path, touch_textures=touch_textures)
+        with w.iword('technique').embed():
+            g = OgreMaterialGenerator(mat, path=path, touch_textures=touch_textures)
 
-    if copy_programs:
-        progs = w.get_active_programs()
-        for prog in progs:
-            if prog.source:
-                prog.save(path)
-            else:
-                print( '[WARNING}: material %s uses program %s which has no source' % (mat.name, prog.name) )
+            if copy_programs:
+                progs = g.get_active_programs()
+                for prog in progs:
+                    if prog.source:
+                        prog.save(path)
+                    else:
+                        print( '[WARNING}: material %s uses program %s which has no source' % (mat.name, prog.name) )
 
-    header = w.get_header()
-    passes = w.get_passes()
+            header = g.get_header()
+            passes = g.get_passes()
 
-    M += '\n'.join(passes)
-    M += indent(1, '}' )      # end technique
-    M += '}\n'                # end material
+            w.write('\n'.join(passes))
 
     if len(header) > 0:
-        return header + '\n' + M
+        return header + '\n' + w.text
     else:
-        return M
+        return w.text
 
 def get_shader_program( name ):
     if name in OgreProgram.PROGRAMS:
             iurl = bpy.path.abspath( texture.image.filepath )
 
         postname = texname = os.path.split(iurl)[-1]
-        destpath = path
 
         if texture.image.packed_file:
             orig = texture.image.filepath
 
         M += indent(3, '}' )
 
+        # copy the texture the the destination path
         if self.touch_textures:
-            # Copy texture only if newer
             if not os.path.isfile(iurl):
                 Report.warnings.append('Missing texture: %s' %iurl )
             else:
-                desturl = os.path.join( destpath, texname )
+                desturl = os.path.join(path, texname)
                 updated = False
-                if not os.path.isfile( desturl ) or os.stat( desturl ).st_mtime < os.stat( iurl ).st_mtime:
-                    f = open( desturl, 'wb' )
-                    f.write( open(iurl,'rb').read() )
-                    f.close()
+                if not os.path.isfile(desturl) or\
+                        os.stat(desturl).st_mtime < os.stat( iurl ).st_mtime:
+                    shutil.copyfile(iurl, desturl)
                     updated = True
-                posturl = os.path.join(destpath,postname)
+                posturl = os.path.join(path, postname)
                 if is_image_postprocessed( texture.image ):
                     if not os.path.isfile( posturl ) or updated:
                         self.image_magick( texture, desturl )   # calls nvconvert if required

io_ogre/ogre/mesh.py

 from .converter import OgreXMLConverter
 from .skeleton import Skeleton
 
-def dot_mesh( ob, path='/tmp', force_name=None, ignore_shape_animation=False, normals=True, isLOD=False):
+def dot_mesh( ob, path, force_name=None, ignore_shape_animation=False, normals=True, isLOD=False, **kwargs):
+    """
+    export the vertices of an object into a .mesh file
+
+    ob: the blender object
+    path: the path to save the .mesh file to. path MUST exist
+    force_name: force a different name for this .mesh
+    kwargs:
+      * material_prefix - string. (optional)
+    """
     start = time.time()
 
+    material_prefix = kwargs.get('material_prefix', '')
+
+    # TODO ensure that before every call the path exists. there are very view calls I believe
     if not os.path.isdir( path ):
         logging.info('>> Creating working directory %s', path )
         os.makedirs( path )
                 "operationtype" : "triangle_list"
             }
             if material_name(mat, False) != "_missing_material_":
-                submesh_attributes['material'] = material_name(mat, False)
+                submesh_attributes['material'] = material_name(mat, False, prefix=material_prefix)
 
             doc.start_tag('submesh', submesh_attributes)
             doc.start_tag('faces', {
         doc.start_tag('submeshnames', {})
         for matidx, mat in enumerate( materials ):
             doc.leaf_tag('submesh', {
-                    'name' : material_name(mat, False),
+                    'name' : material_name(mat, False, prefix=material_prefix),
                     'index' : str(matidx)
             })
         doc.end_tag('submeshnames')
     # Start .mesh.xml to .mesh convertion tool
     OgreXMLConverter(xmlfile, has_uvs=dotextures)
 
-    if arm and CONFIG['ARM_ANIM']:
-        skel = Skeleton( ob )
-        data = skel.to_xml()
-        name = force_name or ob.data.name
-        name = clean_object_name(name)
-        xmlfile = os.path.join(path, '%s.skeleton.xml' % name)
-        f = open( xmlfile, 'wb' )
-        f.write( bytes(data,'utf-8') )
-        f.close()
-        OgreXMLConverter( xmlfile )
+    # note that exporting the skeleton does not happen here anymore
+    # it moved to the function dot_skeleton in its own module
 
     mats = []
     for mat in materials:

io_ogre/ogre/scene.py

Empty file added.

io_ogre/ogre/skeleton.py

 from .. import config
 from ..report import Report
 from ..xml import RDocument
+from .. import util
+from os.path import join
+from .converter import OgreXMLConverter
+
+def dot_skeleton(obj, path, **kwargs):
+    """
+    create the .skeleton file for this object. This is only possible if the object
+    has an armature attached.
+
+    obj: the blender object
+    path: the path where to save this to. Never None and must exist.
+    kwargs:
+      * force_name - string: force another name. default None
+      * invoke_xml_converter - bool: invoke the xml to binary converter. default True
+
+    returns None if there is no skeleton exported, or the filename on success
+    """
+
+    arm = obj.find_armature()
+    if arm and config.get('ARM_ANIM'):
+        skel = Skeleton( obj )
+        name = kwargs.get('force_name') or obj.data.name
+        name = util.clean_object_name(name)
+        xmlfile = join(path, '%s.skeleton.xml' % name)
+        with open(xmlfile, 'wb') as fd:
+            fd.write( bytes(skel.to_xml(),'utf-8') )
+
+        if kwargs.get('invoke_xml_converter', True):
+            OgreXMLConverter( xmlfile )
+        return name + '.skeleton'
+
+    return None
 
 class Bone(object):
 

io_ogre/shader.py

                 r['geoms'].append( geo )
         x += 220
     return r
-

io_ogre/ui/auto_save.py

+import bpy
+from .. import config
+from ..report import Report
+
+def auto_register(register):
+    yield OP_config_autosave
+
+class OP_config_autosave(bpy.types.Operator):
+    '''operator: saves current b2ogre configuration'''
+    bl_idname = "ogre.save_config"
+    bl_label = "save config file"
+    bl_options = {'REGISTER'}
+
+    @classmethod
+    def poll(cls, context):
+        return True
+
+    def invoke(self, context, event):
+        config.save_config()
+        print("save config!!!")
+        Report.reset()
+        Report.messages.append('SAVED %s' %CONFIG_FILEPATH)
+        Report.show()
+        return {'FINISHED'}
+

io_ogre/ui/export.py

     bl_label = "Export Ogre"
     bl_options = {'REGISTER'}
 
+    # export logic is contained in the subclass
+
     # Basic options
     EXPORT_TYPE = 'OGRE'
 
         if child.subcollision and child.name.startswith( prefix ):
             r.append( child )
     return r
+
+class IndentedWriter(object):
+    """
+    Can be used to write well formed documents.
+
+    w = IndentedWriter()
+    with w.word("hello").embed():
+        w.indent().word("world").string("!!!").nl()
+        with w.word("hello").embed():
+            w.iline("schnaps")
+
+    print(w.text)
+    > hello {
+        world "!!!"
+        hello {
+          schnaps
+        }
+      }
+
+
+
+    """
+
+    sym_stack = []
+    text = ""
+    embed_syms = None
+
+    def __init__(self, indent = 0):
+        for i in range(indent):
+            sym_stack.append(None)
+
+    def __enter__(self, **kwargs):
+        begin_sym, end_sym, nl = self.embed_syms
+        self.write(begin_sym)
+        if nl:
+            self.nl()
+        self.sym_stack.append(end_sym)
+
+    def __exit__(self, *kwargs):
+        sym = self.sym_stack.pop()
+        self.indent().write(sym).nl()
+
+    def embed(self, begin_sym="{", end_sym="}", nl=True):
+        self.embed_syms = (begin_sym, end_sym, nl)
+        return self
+
+    def string(self, text):
+        self.write("\"")
+        self.write(text)
+        self.write("\"")
+        return self
+
+    def indent(self, plus=0):
+        return self.write("    " * (len(self.sym_stack) + plus))
+
+    def nl(self):
+        self.write("\n")
+        return self
+
+    def write(self, text):
+        self.text += text
+        return self
+
+    def word(self, text):
+        return self.write(text).write(" ")
+
+    def iwrite(self, text):
+        return self.indent().write(text)
+
+    def iword(self, text):
+        return self.indent().word(text)
+
+    def iline(self, text):
+        return self.indent().line(text)
+
+    def line(self, text):
+        return self.write(text + "\n")
+