Friday, December 2

Dumping Blender Scenes as Lua


Following on to the previous post where I dumped blender scenes as Lisp, I've also written a modified version which is more usual for mainstream use that dumps the entire active object as an enormous Lua literal. I think this would be more useful for mainstream use as the Lua could then be compiled to binary and loaded into a tool, which could pick out and use the useful bits on a per-project basis. My next project is to investigate if such a thing is also possible with FBX & Python - again for more mainstream use.

Once more, here is the source Luke - use it :-)


bl_info = {
"name": "Export Scene as Lua (.lua)",
"author": "John Connors (ZabaQ)",
"version": (0, 7),
"blender": (2, 5, 4),
"api": 33047,
"location": "File > Export",
"description": "Blender Scene To Lua (.lua)",
"warning": "",
"category": "Import-Export"}
import bpy
import mathutils
import types
# ExportHelper is a helper class, defines filename and
# invoke() function which calls the file selector.
from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty, BoolProperty, EnumProperty
# transform python property to lua id
def luaify(s):
if ((s == None) or (s[-1:]==']')):
return None
result = s.split('.')[-1:]
result = result[0].lower()
return result
def escape_lua_string(s):
broken = s.split("\\")
return "\\\\".join(broken)
def introspect_obj(o, txt, indent = 4):
global annotate_file
global outfile
type_o = type(o)
# it has a key associated with it
if (txt != None):
luaname = luaify(txt)
if (luaname != None):
if annotate_file:
print("", file=outfile)
print(" " * indent, end='', file=outfile)
print ( "--[[ " + txt + " " + str(type_o) + "]] ", end = '', file=outfile)
print("", file=outfile)
print(" " * indent, end='', file=outfile)
print ( luaname + " = ", end = '', file=outfile )
# not interested in functions
if (type_o in [ types.CodeType, types.BuiltinFunctionType, types.BuiltinMethodType, types.FunctionType, types.LambdaType, types.MethodType ]):
print ("nil ", end = '', file=outfile)
return
if (str(type_o) == "<class 'bpy_func'>"): # must be a better way to do this
print ("nil ", end = '', file=outfile)
return
# now for the value
# boolean
if (type_o == bool):
if (o == False):
print("false ", end = '', file=outfile)
else:
print("true ", end = '', file=outfile)
return
# None
if (o == None):
print ("nil ", end = '', file=outfile)
return
# Numeric
if (type_o in [ int, float ]):
print(o, " ", end='', file=outfile)
return
# string
if (issubclass(type_o, str)):
print ("\"" + escape_lua_string(o) + "\" ", file=outfile, end='')
return
if txt == None:
return
# we do this explicitly to avoid the madness of swizzling (100s of members per vector!)
if (type_o == mathutils.Vector):
items = [ 'x', 'y', 'z', 'w' ]
print( " { ", end = '' , file=outfile)
for item in items:
newtxt = txt + '.' + item
val = getattr(o, item, None)
if (val != None):
print(" ", end='', file=outfile)
introspect_obj( val, newtxt, indent + 4 )
print(" , ", end='', file=outfile)
print( " } --[[ vec ]] ", end = '' , file=outfile)
return
# object members
try: __members__ = dir(o)
except: __members__ = []
# all kinds of stuff turns up in dir(), so filter it out
if (__members__ != []):
fields = []
for item in __members__:
# internal data
if item.startswith("__"):
continue
# Blenders own type system gets in the way
if item in [ 'rna_type', 'bl_rna', 'owner' ]:
continue
if item in txt:
continue
# we want data, not funcitions
type_i = type(getattr(o, item, None))
if (type_i in [ types.CodeType, types.BuiltinFunctionType, types.BuiltinMethodType, types.FunctionType, types.LambdaType ]):
continue
if (str(type_i) == "<class 'bpy_func'>"): # must be a better way to do this
continue
fields += [ item ]
# if there's anything left, print it
if (len(fields) != 0):
print("", end = '', file=outfile)
print(" " * indent, end='', file=outfile)
print("{", end = '' , file=outfile)
itemindex = 0
fi = len(fields)
for item in fields:
newtxt = txt + '.' + item
# print(" -- %s " % newtxt, end='', file=outfile)
itemindex = itemindex + 1
introspect_obj( getattr(o, item, None), newtxt, indent + 4)
fi = fi - 1
if (fi > 0):
print(" , ", end='', file=outfile)
print( " } --[[ fields ]] ", end='', file=outfile)
return
# now, try dict types
try: keys = o.keys()
except: keys = None
if keys:
print( "{", end = '' , file=outfile)
ik = len(keys)
for k in keys:
newtxt = txt + "[" + k + "]"
ik = ik - 1
type_i = type(o.__getitem__(k))
print(" %s = " % k, end='', file=outfile)
introspect_obj(o.__getitem__(k), newtxt, indent+4)
if (ik > 0):
print(" , ", end='', file=outfile)
print( " } --[[ keys ]] ", end='', file=outfile)
else:
# list/tuple
try: length = len( o )
except: length = -1
if (length != -1):
# indexable?
if ("__getitem__" in __members__):
# print(" " * indent, end='', file=outfile)
# print(txt)
# lets not dump entire textures > 4k in ascii, m'kay :-)
print( " { ", end = '' , file=outfile)
if (not("pixels" in txt) or (length < 4 * 1024)):
for i in range(length):
print(" ", end='', file=outfile)
newtxt = txt + '[' + str(i) + ']'
# print(" -- %s " % newtxt, end='', file=outfile)
print(" [%d] = " % i, end='', file=outfile)
introspect_obj(o[i], newtxt, indent+4)
if (i < length -1):
print(" , ", end='', file=outfile)
print( " } --[[ array ]] " , end = '', file=outfile)
else:
# sets/nonindexable
# print(" " * indent, end='', file=outfile)
print( " { ", end = '' , file=outfile)
ic = len(o)
for i in o:
introspect_obj(i, None, indent+4)
ic = ic - 1
if (ic > 0):
print(" , ", end='', file=outfile)
print( " } --[[ set ]] ", end='', file=outfile)
return
def dump_lua(fn, package, introspect, annotate):
global outfile
global annotate_file
outfile = open(fn, 'w+')
print ("%s = {" % package, end='', file=outfile)
annotate_file = annotate
introspect_obj(eval(introspect, globals(), locals()), introspect)
print ("}", file=outfile)
outfile.close()
class Export_Lua(bpy.types.Operator, ExportHelper):
'''Export secene as structured Lua Table'''
bl_idname = "export.lua"
bl_label = "Export Lua"
filename_ext = ".lua"
filter_glob = StringProperty(default="*.lua", options={'HIDDEN'})
filepath = StringProperty(name="File Path", description="Filepath used for exporting the lua file", maxlen= 1024, default= "", subtype='FILE_PATH')
check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True)
packageName = StringProperty(name="VarName", description="Name of variable to assign scene to.", maxlen=256,default="scene")
introspectName = StringProperty(name="Data", description="Data to introspect", maxlen=256, default="bpy.context.active_object")
annotateFlag = BoolProperty(name="Annotate", description="Annotate file with comments (makes it large!)", default=False)
@classmethod
def poll(cls, context):
return context.active_object != None
def execute(self, context):
dump_lua(self.filepath, self.packageName, self.introspectName, self.annotateFlag)
return {'FINISHED'}
### REGISTER ###
def menu_func(self, context):
self.layout.operator(Export_Lua.bl_idname, text="Lua (.lua)")
def register():
bpy.utils.register_class(Export_Lua)
bpy.types.INFO_MT_file_export.append(menu_func)
#bpy.types.VIEW3D_PT_tools_objectmode.prepend(menu_func)
def unregister():
bpy.utils.unregister_class(Export_Lua)
bpy.types.INFO_MT_file_export.remove(menu_func)
if __name__ == "__main__":
register()
# test call
bpy.ops.export.Lua('INVOKE_DEFAULT')

No comments: