summaryrefslogtreecommitdiff
path: root/tools/export
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
commit0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch)
tree276c4d099e178eb67fbd14f61d77b05e3808e9e3 /tools/export
parent0e49da1687bc8192ed210947da52c9e5c5f301bb (diff)
GODOT IS OPEN SOURCE
Diffstat (limited to 'tools/export')
-rw-r--r--tools/export/blender25/io_scene_dae/__init__.py180
-rw-r--r--tools/export/blender25/io_scene_dae/export_dae.py1217
-rw-r--r--tools/export/export_lua.py209
-rw-r--r--tools/export/godot_export.py1842
4 files changed, 3448 insertions, 0 deletions
diff --git a/tools/export/blender25/io_scene_dae/__init__.py b/tools/export/blender25/io_scene_dae/__init__.py
new file mode 100644
index 0000000000..39d8e94a53
--- /dev/null
+++ b/tools/export/blender25/io_scene_dae/__init__.py
@@ -0,0 +1,180 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+bl_info = {
+ "name": "Khronos Collada format",
+ "author": "Juan Linietsky",
+ "blender": (2, 5, 8),
+ "api": 38691,
+ "location": "File > Import-Export",
+ "description": ("Export DAE Scenes"),
+ "warning": "",
+ "wiki_url": ("None"),
+ "tracker_url": "",
+ "support": 'OFFICIAL',
+ "category": "Import-Export"}
+
+
+if "bpy" in locals():
+ import imp
+ if "export_dae" in locals():
+ imp.reload(export_dae)
+
+
+import bpy
+from bpy.props import StringProperty, BoolProperty, FloatProperty, EnumProperty
+
+from bpy_extras.io_utils import (ExportHelper,
+ path_reference_mode,
+ axis_conversion,
+ )
+
+
+class ExportDAE(bpy.types.Operator, ExportHelper):
+ '''Selection to DAE'''
+ bl_idname = "export_scene.dae"
+ bl_label = "Export DAE"
+ bl_options = {'PRESET'}
+
+ filename_ext = ".dae"
+ filter_glob = StringProperty(default="*.dae", options={'HIDDEN'})
+
+ # List of operator properties, the attributes will be assigned
+ # to the class instance from the operator settings before calling.
+
+
+ object_types = EnumProperty(
+ name="Object Types",
+ options={'ENUM_FLAG'},
+ items=(('EMPTY', "Empty", ""),
+ ('CAMERA', "Camera", ""),
+ ('LAMP', "Lamp", ""),
+ ('ARMATURE', "Armature", ""),
+ ('MESH', "Mesh", ""),
+ ('CURVE', "Curve", ""),
+ ),
+ default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH','CURVE'},
+ )
+
+ use_export_selected = BoolProperty(
+ name="Selected Objects",
+ description="Export only selected objects (and visible in active layers if that applies).",
+ default=False,
+ )
+ use_mesh_modifiers = BoolProperty(
+ name="Apply Modifiers",
+ description="Apply modifiers to mesh objects (on a copy!).",
+ default=True,
+ )
+ use_copy_images = BoolProperty(
+ name="Copy Images",
+ description="Copy Images (create images/ subfolder)",
+ default=False,
+ )
+ use_active_layers = BoolProperty(
+ name="Active Layers",
+ description="Export only objects on the active layers.",
+ default=True,
+ )
+ use_exclude_ctrl_bones = BoolProperty(
+ name="Exclude Control Bones",
+ description="Exclude skeleton bones with names that begin with 'ctrl'.",
+ default=True,
+ )
+ use_anim = BoolProperty(
+ name="Export Animation",
+ description="Export keyframe animation",
+ default=False,
+ )
+ use_anim_action_all = BoolProperty(
+ name="All Actions",
+ description=("Export all actions for the first armature found in separate DAE files"),
+ default=False,
+ )
+ use_anim_optimize = BoolProperty(
+ name="Optimize Keyframes",
+ description="Remove double keyframes",
+ default=True,
+ )
+ anim_optimize_precision = FloatProperty(
+ name="Precision",
+ description=("Tolerence for comparing double keyframes "
+ "(higher for greater accuracy)"),
+ min=1, max=16,
+ soft_min=1, soft_max=16,
+ default=6.0,
+ )
+ use_metadata = BoolProperty(
+ name="Use Metadata",
+ default=True,
+ options={'HIDDEN'},
+ )
+
+ @property
+ def check_extension(self):
+ return True#return self.batch_mode == 'OFF'
+
+ def check(self, context):
+ return True
+ """
+ isretur_def_change = super().check(context)
+ return (is_xna_change or is_def_change)
+ """
+
+ def execute(self, context):
+ if not self.filepath:
+ raise Exception("filepath not set")
+
+ """ global_matrix = Matrix()
+
+ global_matrix[0][0] = \
+ global_matrix[1][1] = \
+ global_matrix[2][2] = self.global_scale
+ """
+
+ keywords = self.as_keywords(ignore=("axis_forward",
+ "axis_up",
+ "global_scale",
+ "check_existing",
+ "filter_glob",
+ "xna_validate",
+ ))
+
+ from . import export_dae
+ return export_dae.save(self, context, **keywords)
+
+
+def menu_func(self, context):
+ self.layout.operator(ExportDAE.bl_idname, text="Khronos Collada (.dae)")
+
+
+def register():
+ bpy.utils.register_module(__name__)
+
+ bpy.types.INFO_MT_file_export.append(menu_func)
+
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
+
+ bpy.types.INFO_MT_file_export.remove(menu_func)
+
+if __name__ == "__main__":
+ register()
diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py
new file mode 100644
index 0000000000..1a0cb37a17
--- /dev/null
+++ b/tools/export/blender25/io_scene_dae/export_dae.py
@@ -0,0 +1,1217 @@
+# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+# Script copyright (C) Juan Linietsky
+# Contact Info: juan@codenix.com
+
+"""
+This script is an exporter to the Khronos Collada file format.
+
+http://www.khronos.org/collada/
+"""
+
+# TODO:
+# Materials & Textures
+# Optionally export Vertex Colors
+# Morph Targets
+# Control bone removal
+# Copy textures
+# Export Keyframe Optimization
+# --
+# Morph Targets
+# Blender native material? (?)
+
+import os
+import time
+import math # math.pi
+import shutil
+import bpy
+from mathutils import Vector, Matrix
+
+#according to collada spec, order matters
+S_ASSET=0
+S_IMGS=1
+S_FX=2
+S_MATS=3
+S_GEOM=4
+S_CONT=5
+S_CAMS=6
+S_LAMPS=7
+S_ANIM_CLIPS=8
+S_NODES=9
+S_ANIM=10
+
+CMP_EPSILON=0.0001
+
+def snap_tup(tup):
+ ret=()
+ for x in tup:
+ ret+=( x-math.fmod(x,0.0001), )
+
+ return tup
+
+
+def strmtx(mtx):
+ s=" "
+ for x in range(4):
+ for y in range(4):
+ s+=str(mtx[x][y])
+ s+=" "
+ s+=" "
+ return s
+
+def numarr(a,mult=1.0):
+ s=" "
+ for x in a:
+ s+=" "+str(x*mult)
+ s+=" "
+ return s
+
+def strarr(arr):
+ s=" "
+ for x in arr:
+ s+=" "+str(x)
+ s+=" "
+ return s
+
+
+
+class DaeExporter:
+
+ def validate_id(self,d):
+ if (d.find("id-")==0):
+ return "z"+d
+ return d
+
+
+ def new_id(self,t):
+ self.last_id+=1
+ return "id-"+t+"-"+str(self.last_id)
+
+ class Vertex:
+
+ def close_to(v):
+ if ( (self.vertex-v.vertex).length() > CMP_EPSILON ):
+ return False
+ if ( (self.normal-v.normal).length() > CMP_EPSILON ):
+ return False
+ if ( (self.uv-v.uv).length() > CMP_EPSILON ):
+ return False
+ if ( (self.uv2-v.uv2).length() > CMP_EPSILON ):
+ return False
+
+ return True
+
+ def get_tup(self):
+ tup = (self.vertex.x,self.vertex.y,self.vertex.z,self.normal.x,self.normal.y,self.normal.z)
+ for t in self.uv:
+ tup = tup + (t.x,t.y)
+ return tup
+
+ def __init__(self):
+ self.vertex = Vector( (0.0,0.0,0.0) )
+ self.normal = Vector( (0.0,0.0,0.0) )
+ self.color = Vector( (0.0,0.0,0.0) )
+ self.uv = []
+ self.uv2 = Vector( (0.0,0.0) )
+ self.bones=[]
+ self.weights=[]
+
+
+ def writel(self,section,indent,text):
+ if (not (section in self.sections)):
+ self.sections[section]=[]
+ line=""
+ for x in range(indent):
+ line+="\t"
+ line+=text
+ self.sections[section].append(line)
+
+
+ def export_image(self,image):
+
+ if (image in self.image_cache):
+ return self.image_cache[image]
+
+ imgpath = image.filepath
+ if (imgpath.find("//")==0 or imgpath.find("\\\\")==0):
+ #if relative, convert to absolute
+ imgpath = bpy.path.abspath(imgpath)
+
+ #path is absolute, now do something!
+
+ if (self.config["use_copy_images"]):
+ #copy image
+ basedir = os.path.dirname(self.path)+"/images"
+ if (not os.path.isdir(basedir)):
+ os.makedirs(basedir)
+ dstfile=basedir+"/"+os.path.basename(imgpath)
+ if (not os.path.isfile(dstfile)):
+ shutil.copy(imgpath,dstfile)
+ imgpath="images/"+os.path.basename(imgpath)
+
+ else:
+ #export relative, always, no one wants absolute paths.
+ imgpath = os.path.relpath(imgpath,os.path.dirname(self.path)).replace("\\","/") # export unix compatible always
+
+
+ imgid = self.new_id("image")
+ self.writel(S_IMGS,1,'<image id="'+imgid+'" name="'+image.name+'">')
+ self.writel(S_IMGS,2,'<init_from>'+imgpath+'</init_from>"/>')
+ self.writel(S_IMGS,1,'</image>')
+ self.image_cache[image]=imgid
+ return imgid
+
+ def export_material(self,material,double_sided_hint=True):
+
+ if (material in self.material_cache):
+ return self.material_cache[material]
+
+ fxid = self.new_id("fx")
+ self.writel(S_FX,1,'<effect id="'+fxid+'" name="'+material.name+'-fx">')
+ self.writel(S_FX,2,'<profile_COMMON>')
+
+ #Find and fetch the textures and create sources
+ sampler_table={}
+ diffuse_tex=None
+ specular_tex=None
+ emission_tex=None
+ normal_tex=None
+ for i in range(len(material.texture_slots)):
+ ts=material.texture_slots[i]
+ if (not ts):
+ continue
+ if (not ts.use):
+ continue
+ if (not ts.texture):
+ continue
+ if (ts.texture.type!="IMAGE"):
+ continue
+
+ if (ts.texture.image==None):
+ continue
+
+ #image
+ imgid = self.export_image(ts.texture.image)
+
+ #surface
+ surface_sid = self.new_id("fx_surf")
+ self.writel(S_FX,3,'<newparam sid="'+surface_sid+'">')
+ self.writel(S_FX,4,'<surface type="2D">')
+ self.writel(S_FX,5,'<init_from>'+imgid+'</init_from>') #this is sooo weird
+ self.writel(S_FX,5,'<format>A8R8G8B8</format>')
+ self.writel(S_FX,4,'</surface>')
+ self.writel(S_FX,3,'</newparam>')
+ #sampler, collada sure likes it difficult
+ sampler_sid = self.new_id("fx_sampler")
+ self.writel(S_FX,3,'<newparam sid="'+sampler_sid+'">')
+ self.writel(S_FX,4,'<sampler2D>')
+ self.writel(S_FX,5,'<source>'+surface_sid+'</source>')
+ self.writel(S_FX,4,'</sampler2D>')
+ self.writel(S_FX,3,'</newparam>')
+ sampler_table[i]=sampler_sid
+
+ if (ts.use_map_color_diffuse and diffuse_tex==None):
+ diffuse_tex=sampler_sid
+ if (ts.use_map_color_spec and specular_tex==None):
+ specular_tex=sampler_sid
+ if (ts.use_map_emit and emission_tex==None):
+ emission_tex=sampler_sid
+ if (ts.use_map_normal and normal_tex==None):
+ normal_tex=sampler_sid
+
+ self.writel(S_FX,3,'<technique sid="common">')
+ shtype="blinn"
+ self.writel(S_FX,4,'<'+shtype+'>')
+ #ambient? from where?
+
+ self.writel(S_FX,5,'<emission>')
+ if (emission_tex!=None):
+ self.writel(S_FX,6,'<texture texture="'+emission_tex+'" texcoord="CHANNEL1"/>')
+ else:
+ self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.emit)+' </color>') # not totally right but good enough
+ self.writel(S_FX,5,'</emission>')
+
+ self.writel(S_FX,5,'<ambient>')
+ self.writel(S_FX,6,'<color>'+numarr(self.scene.world.ambient_color,material.ambient)+' </color>')
+ self.writel(S_FX,5,'</ambient>')
+
+ self.writel(S_FX,5,'<diffuse>')
+ if (diffuse_tex!=None):
+ self.writel(S_FX,6,'<texture texture="'+diffuse_tex+'" texcoord="CHANNEL1"/>')
+ else:
+ self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.diffuse_intensity)+'</color>')
+ self.writel(S_FX,5,'</diffuse>')
+
+ self.writel(S_FX,5,'<specular>')
+ if (specular_tex!=None):
+ self.writel(S_FX,6,'<texture texture="'+specular_tex+'" texcoord="CHANNEL1"/>')
+ else:
+ self.writel(S_FX,6,'<color>'+numarr(material.specular_color,material.specular_intensity)+'</color>')
+ self.writel(S_FX,5,'</specular>')
+
+ self.writel(S_FX,5,'<shininess>')
+ self.writel(S_FX,6,'<float>'+str(material.specular_hardness)+'</float>')
+ self.writel(S_FX,5,'</shininess>')
+
+ self.writel(S_FX,5,'<reflective>')
+ self.writel(S_FX,6,'<color>'+strarr(material.mirror_color)+'</color>')
+ self.writel(S_FX,5,'</reflective>')
+
+ if (material.use_transparency):
+ self.writel(S_FX,5,'<transparency>')
+ self.writel(S_FX,6,'<float>'+str(material.alpha)+'</float>')
+ self.writel(S_FX,5,'</transparency>')
+
+
+ self.writel(S_FX,5,'<index_of_refraction>'+str(material.specular_ior)+'</index_of_refraction>')
+
+ self.writel(S_FX,4,'</'+shtype+'>')
+
+ self.writel(S_FX,4,'<extra>')
+ self.writel(S_FX,5,'<technique profile="FCOLLADA">')
+ if (normal_tex):
+ self.writel(S_FX,6,'<bump bumptype="NORMALMAP">')
+ self.writel(S_FX,7,'<texture texture="'+normal_tex+'" texcoord="CHANNEL1"/>')
+ self.writel(S_FX,6,'</bump>')
+
+ self.writel(S_FX,5,'</technique>')
+ self.writel(S_FX,4,'</extra>')
+
+ self.writel(S_FX,3,'</technique>')
+ self.writel(S_FX,2,'</profile_COMMON>')
+ self.writel(S_FX,1,'</effect>')
+
+ # Also export blender material in all it's glory (if set as active)
+
+
+ #Material
+ matid = self.new_id("material")
+ self.writel(S_MATS,1,'<material id="'+matid+'" name="'+material.name+'">')
+ self.writel(S_MATS,2,'<instance_effect url="#'+fxid+'"/>')
+ self.writel(S_MATS,1,'</material>')
+
+ self.material_cache[material]=matid
+ return matid
+
+
+ def export_mesh(self,node,armature=None):
+
+ if (len(node.modifiers) and self.config["use_mesh_modifiers"]):
+ mesh=node.to_mesh(self.scene,True,"RENDER") #is this allright?
+ else:
+ mesh=node.data
+
+ if (mesh in self.mesh_cache):
+ return self.mesh_cache[mesh]
+
+ mesh.update(calc_tessface=True)
+ vertices=[]
+ vertex_map={}
+ surface_indices={}
+ materials={}
+
+ materials={}
+
+ si=None
+ if (armature!=None):
+ si=self.skeleton_info[armature]
+
+ has_uv=False
+ has_uv2=False
+ has_weights=armature!=None
+ has_colors=False
+ mat_assign=[]
+
+ uv_layer_count=len(mesh.uv_textures)
+
+ for fi in range(len(mesh.tessfaces)):
+ f=mesh.tessfaces[fi]
+
+
+ if (not (f.material_index in surface_indices)):
+ surface_indices[f.material_index]=[]
+ print("Type: "+str(type(f.material_index)))
+ print("IDX: "+str(f.material_index)+"/"+str(len(mesh.materials)))
+
+
+ try:
+ #Bizarre blender behavior i don't understand, so catching exception
+ mat = mesh.materials[f.material_index]
+ except:
+ mat= None
+
+ if (mat!=None):
+ materials[f.material_index]=self.export_material( mat )
+ else:
+ materials[f.material_index]=None #weird, has no material?
+
+ indices = surface_indices[f.material_index]
+ vi=[]
+ #make triangles always
+ if (len(f.vertices)==3):
+ vi.append(0)
+ vi.append(1)
+ vi.append(2)
+ elif (len(f.vertices)==4):
+ vi.append(0)
+ vi.append(1)
+ vi.append(2)
+ vi.append(0)
+ vi.append(2)
+ vi.append(3)
+
+ for x in vi:
+ mv = mesh.vertices[f.vertices[x]]
+
+ v = self.Vertex()
+ v.vertex = Vector( mv.co )
+
+ for xt in mesh.tessface_uv_textures:
+ d = xt.data[fi]
+ uvsrc = [d.uv1,d.uv2,d.uv3,d.uv4]
+ v.uv.append( Vector( uvsrc[x] ) )
+
+
+ if (f.use_smooth):
+ v.normal=Vector( mv.normal )
+ else:
+ v.normal=Vector( f.normal )
+
+ # if (armature):
+ # v.vertex = node.matrix_world * v.vertex
+
+ #v.color=Vertex(mv. ???
+
+ if (armature!=None):
+ wsum=0.0
+ for vg in mv.groups:
+ if vg.group >= len(node.vertex_groups):
+ continue;
+ name = node.vertex_groups[vg.group].name
+ if (name in si["bone_index"]):
+ if (vg.weight>0.001): #blender has a lot of zero weight stuff
+ v.bones.append(si["bone_index"][name])
+ v.weights.append(vg.weight)
+ wsum+=vg.weight
+
+
+ tup = v.get_tup()
+ idx = 0
+ if (tup in vertex_map):
+ idx = vertex_map[tup]
+ else:
+ idx = len(vertices)
+ vertices.append(v)
+ vertex_map[tup]=idx
+
+ indices.append(idx)
+
+ meshid = self.new_id("mesh")
+
+ self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'">')
+ self.writel(S_GEOM,2,'<mesh>')
+
+
+ # Vertex Array
+ self.writel(S_GEOM,3,'<source id="'+meshid+'-positions">')
+ float_values=""
+ for v in vertices:
+ float_values+=" "+str(v.vertex.x)+" "+str(v.vertex.y)+" "+str(v.vertex.z)
+ self.writel(S_GEOM,4,'<float_array id="'+meshid+'-positions-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-positions-array" count="'+str(len(vertices))+'" stride="3">')
+ self.writel(S_GEOM,5,'<param name="X" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,4,'</technique_common>')
+ self.writel(S_GEOM,3,'</source>')
+
+ # Normal Array
+
+ self.writel(S_GEOM,3,'<source id="'+meshid+'-normals">')
+ float_values=""
+ for v in vertices:
+ float_values+=" "+str(v.normal.x)+" "+str(v.normal.y)+" "+str(v.normal.z)
+ self.writel(S_GEOM,4,'<float_array id="'+meshid+'-normals-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-normals-array" count="'+str(len(vertices))+'" stride="3">')
+ self.writel(S_GEOM,5,'<param name="X" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,4,'</technique_common>')
+ self.writel(S_GEOM,3,'</source>')
+
+ # UV Arrays
+
+ for uvi in range(uv_layer_count):
+
+ self.writel(S_GEOM,3,'<source id="'+meshid+'-texcoord-'+str(uvi)+'">')
+ float_values=""
+ for v in vertices:
+ float_values+=" "+str(v.uv[uvi].x)+" "+str(v.uv[uvi].y)
+ self.writel(S_GEOM,4,'<float_array id="'+meshid+'-texcoord-'+str(uvi)+'-array" count="'+str(len(vertices)*2)+'">'+float_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-texcoord-'+str(uvi)+'-array" count="'+str(len(vertices))+'" stride="2">')
+ self.writel(S_GEOM,5,'<param name="S" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="T" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,4,'</technique_common>')
+ self.writel(S_GEOM,3,'</source>')
+
+ # Triangle Lists
+ self.writel(S_GEOM,3,'<vertices id="'+meshid+'-vertices">')
+ self.writel(S_GEOM,4,'<input semantic="POSITION" source="#'+meshid+'-positions"/>')
+ self.writel(S_GEOM,3,'</vertices>')
+
+ for m in surface_indices:
+ indices = surface_indices[m]
+ mat = materials[m]
+ if (mat!=None):
+ matref = self.new_id("trimat")
+ self.writel(S_GEOM,3,'<triangles count="'+str(int(len(indices)/3))+'" material="'+matref+'">') # todo material
+ mat_assign.append( (mat,matref) )
+ else:
+ self.writel(S_GEOM,3,'<triangles count="'+str(int(len(indices)/3))+'">') # todo material
+ self.writel(S_GEOM,4,'<input semantic="VERTEX" source="#'+meshid+'-vertices" offset="0"/>')
+ self.writel(S_GEOM,4,'<input semantic="NORMAL" source="#'+meshid+'-normals" offset="1"/>')
+ extra_indices=0
+ for uvi in range(uv_layer_count):
+ self.writel(S_GEOM,4,'<input semantic="TEXCOORD" source="#'+meshid+'-texcoord-'+str(uvi)+'" offset="'+str(2+uvi)+'" set="'+str(uvi)+'"/>')
+ extra_indices+=1
+
+ int_values="<p>"
+ for i in range(len(indices)):
+ int_values+=" "+str(indices[i]) # vertex index
+ int_values+=" "+str(indices[i]) # normal index
+ for e in range(extra_indices):
+ int_values+=" "+str(indices[i]) # normal index
+ int_values+="</p>"
+ self.writel(S_GEOM,4,int_values)
+ self.writel(S_GEOM,3,'</triangles>')
+
+
+ self.writel(S_GEOM,2,'</mesh>')
+ self.writel(S_GEOM,1,'</geometry>')
+
+ meshdata={}
+ meshdata["id"]=meshid
+ meshdata["material_assign"]=mat_assign
+ self.mesh_cache[mesh]=meshdata
+
+
+ # Export armature data (if armature exists)
+
+ if (armature!=None):
+
+ contid = self.new_id("controller")
+
+ self.writel(S_CONT,1,'<controller id="'+contid+'">')
+ self.writel(S_CONT,2,'<skin source="'+meshid+'">')
+ self.writel(S_CONT,3,'<bind_shape_matrix>'+strmtx(node.matrix_world)+'</bind_shape_matrix>')
+ #Joint Names
+ self.writel(S_CONT,3,'<source id="'+contid+'-joints">')
+ name_values=""
+ for v in si["bone_names"]:
+ name_values+=" "+v
+
+ self.writel(S_CONT,4,'<Name_array id="'+contid+'-joints-array" count="'+str(len(si["bone_names"]))+'">'+name_values+'</Name_array>')
+ self.writel(S_CONT,4,'<technique_common>')
+ self.writel(S_CONT,4,'<accessor source="#'+contid+'-joints-array" count="'+str(len(si["bone_names"]))+'" stride="1">')
+ self.writel(S_CONT,5,'<param name="JOINT" type="Name"/>')
+ self.writel(S_CONT,4,'</accessor>')
+ self.writel(S_CONT,4,'</technique_common>')
+ self.writel(S_CONT,3,'</source>')
+ #Pose Matrices!
+ self.writel(S_CONT,3,'<source id="'+contid+'-bind_poses">')
+ pose_values=""
+ for v in si["bone_bind_poses"]:
+ pose_values+=" "+strmtx(v)
+
+ self.writel(S_CONT,4,'<float_array id="'+contid+'-bind_poses-array" count="'+str(len(si["bone_bind_poses"])*16)+'">'+pose_values+'</float_array>')
+ self.writel(S_CONT,4,'<technique_common>')
+ self.writel(S_CONT,4,'<accessor source="#'+contid+'-bind_poses-array" count="'+str(len(si["bone_bind_poses"]))+'" stride="16">')
+ self.writel(S_CONT,5,'<param name="TRANSFORM" type="float4x4"/>')
+ self.writel(S_CONT,4,'</accessor>')
+ self.writel(S_CONT,4,'</technique_common>')
+ self.writel(S_CONT,3,'</source>')
+ #Skin Weights!
+ self.writel(S_CONT,3,'<source id="'+contid+'-skin_weights">')
+ skin_weights=""
+ skin_weights_total=0
+ for v in vertices:
+ skin_weights_total+=len(v.weights)
+ for w in v.weights:
+ skin_weights+=" "+str(w)
+
+ self.writel(S_CONT,4,'<float_array id="'+contid+'-skin_weights-array" count="'+str(skin_weights_total)+'">'+skin_weights+'</float_array>')
+ self.writel(S_CONT,4,'<technique_common>')
+ self.writel(S_CONT,4,'<accessor source="#'+contid+'-skin_weights-array" count="'+str(skin_weights_total)+'" stride="1">')
+ self.writel(S_CONT,5,'<param name="WEIGHT" type="float"/>')
+ self.writel(S_CONT,4,'</accessor>')
+ self.writel(S_CONT,4,'</technique_common>')
+ self.writel(S_CONT,3,'</source>')
+
+
+ self.writel(S_CONT,3,'<joints>')
+ self.writel(S_CONT,4,'<input semantic="JOINT" source="#'+contid+'-joints"/>')
+ self.writel(S_CONT,4,'<input semantic="INV_BIND_MATRIX" source="#'+contid+'-bind_poses"/>')
+ self.writel(S_CONT,3,'</joints>')
+ self.writel(S_CONT,3,'<vertex_weights count="'+str(len(vertices))+'">')
+ self.writel(S_CONT,4,'<input semantic="JOINT" source="#'+contid+'-joints" offset="0"/>')
+ self.writel(S_CONT,4,'<input semantic="WEIGHT" source="#'+contid+'-skin_weights" offset="1"/>')
+ vcounts=""
+ vs=""
+ vcount=0
+ for v in vertices:
+ vcounts+=" "+str(len(v.weights))
+ for b in v.bones:
+ vs+=" "+str(b)
+ vs+=" "+str(vcount)
+ vcount+=1
+ self.writel(S_CONT,4,'<vcount>'+vcounts+'</vcount>')
+ self.writel(S_CONT,4,'<v>'+vs+'</v>')
+ self.writel(S_CONT,3,'</vertex_weights>')
+
+
+ self.writel(S_CONT,2,'</skin>')
+ self.writel(S_CONT,1,'</controller>')
+ meshdata["skin_id"]=contid
+
+
+ return meshdata
+
+
+ def export_mesh_node(self,node,il):
+
+ if (node.data==None):
+ return
+ armature=None
+
+ if (node.parent!=None):
+ if (node.parent.type=="ARMATURE"):
+ armature=node.parent
+
+
+ meshdata = self.export_mesh(node,armature)
+
+ if (armature==None):
+ self.writel(S_NODES,il,'<instance_geometry url="#'+meshdata["id"]+'">')
+ else:
+ self.writel(S_NODES,il,'<instance_controller url="#'+meshdata["skin_id"]+'">')
+ for sn in self.skeleton_info[armature]["skeleton_nodes"]:
+ self.writel(S_NODES,il+1,'<skeleton>#'+sn+'</skeleton>')
+
+
+ if (len(meshdata["material_assign"])>0):
+
+ self.writel(S_NODES,il+1,'<bind_material>')
+ self.writel(S_NODES,il+2,'<technique_common>')
+ for m in meshdata["material_assign"]:
+ self.writel(S_NODES,il+3,'<instance_material symbol="'+m[1]+'" target="#'+m[0]+'"/>')
+
+ self.writel(S_NODES,il+2,'</technique_common>')
+ self.writel(S_NODES,il+1,'</bind_material>')
+
+ if (armature==None):
+ self.writel(S_NODES,il,'</instance_geometry>')
+ else:
+ self.writel(S_NODES,il,'</instance_controller>')
+
+
+ def export_armature_bone(self,bone,il,si):
+ boneid = self.new_id("bone")
+ boneidx = si["bone_count"]
+ si["bone_count"]+=1
+ bonesid = si["name"]+"-"+str(boneidx)
+ si["bone_index"][bone.name]=boneidx
+ si["bone_ids"][bone]=boneid
+ si["bone_names"].append(bonesid)
+ self.writel(S_NODES,il,'<node id="'+boneid+'" sid="'+bonesid+'" name="'+bone.name+'" type="JOINT">')
+ il+=1
+ xform = bone.matrix_local
+ si["bone_bind_poses"].append((si["armature_xform"] * xform).inverted())
+
+ if (bone.parent!=None):
+ xform = bone.parent.matrix_local.inverted() * xform
+ else:
+ si["skeleton_nodes"].append(boneid)
+
+ self.writel(S_NODES,il,'<matrix sid="transform">'+strmtx(xform)+'</matrix>')
+ for c in bone.children:
+ self.export_armature_bone(c,il,si)
+ il-=1
+ self.writel(S_NODES,il,'</node>')
+
+
+ def export_armature_node(self,node,il):
+
+ if (node.data==None):
+ return
+
+ self.skeletons.append(node)
+
+ armature = node.data
+ self.skeleton_info[node]={ "bone_count":0, "name":node.name, "bone_index":{},"bone_ids":{},"bone_names":[],"bone_bind_poses":[],"skeleton_nodes":[],"armature_xform":node.matrix_world }
+
+
+
+ for b in armature.bones:
+ if (b.parent!=None):
+ continue
+ self.export_armature_bone(b,il,self.skeleton_info[node])
+
+ if (node.pose):
+ for b in node.pose.bones:
+ for x in b.constraints:
+ if (x.type=='ACTION'):
+ self.action_constraints.append(x.action)
+
+
+ def export_camera_node(self,node,il):
+
+ if (node.data==None):
+ return
+
+ camera=node.data
+ camid=self.new_id("camera")
+ self.writel(S_CAMS,1,'<camera id="'+camid+'" name="'+camera.name+'">')
+ self.writel(S_CAMS,2,'<optics>')
+ self.writel(S_CAMS,3,'<technique_common>')
+ if (camera.type=="PERSP"):
+ self.writel(S_CAMS,4,'<perspective>')
+ self.writel(S_CAMS,5,'<yfov> '+str(math.degrees(camera.angle))+' </yfov>') # I think?
+ self.writel(S_CAMS,5,'<aspect_ratio> '+str(self.scene.render.resolution_x / self.scene.render.resolution_y)+' </aspect_ratio>')
+ self.writel(S_CAMS,5,'<znear> '+str(camera.clip_start)+' </znear>')
+ self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>')
+ self.writel(S_CAMS,4,'</perspective>')
+ else:
+ self.writel(S_CAMS,4,'<orthografic>')
+ self.writel(S_CAMS,5,'<xmag> '+str(camera.ortho_scale)+' </xmag>') # I think?
+ self.writel(S_CAMS,5,'<aspect_ratio> '+str(self.scene.render.resolution_x / self.scene.render.resolution_y)+' </aspect_ratio>')
+ self.writel(S_CAMS,5,'<znear> '+str(camera.clip_start)+' </znear>')
+ self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>')
+ self.writel(S_CAMS,4,'</orthografic>')
+
+ self.writel(S_CAMS,3,'</technique_common>')
+ self.writel(S_CAMS,2,'</optics>')
+ self.writel(S_CAMS,1,'</camera>')
+
+
+ self.writel(S_NODES,il,'<instance_camera url="#'+camid+'"/>')
+
+ def export_lamp_node(self,node,il):
+
+ if (node.data==None):
+ return
+
+ light=node.data
+ lightid=self.new_id("light")
+ self.writel(S_LAMPS,1,'<light id="'+lightid+'" name="'+light.name+'">')
+ self.writel(S_LAMPS,2,'<optics>')
+ self.writel(S_LAMPS,3,'<technique_common>')
+
+ if (light.type=="POINT" or light.type=="HEMI"):
+ self.writel(S_LAMPS,4,'<point>')
+ self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>')
+ att_by_distance = 2.0 / light.distance # convert to linear attenuation
+ self.writel(S_LAMPS,5,'<linear_attenuation>'+str(att_by_distance)+'</linear_attenuation>')
+ if (light.use_sphere):
+ self.writel(S_LAMPS,5,'<zfar>'+str(light.distance)+'</zfar>')
+
+ self.writel(S_LAMPS,4,'</point>')
+ elif (light.type=="SPOT"):
+ self.writel(S_LAMPS,4,'<spot>')
+ self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>')
+ att_by_distance = 2.0 / light.distance # convert to linear attenuation
+ self.writel(S_LAMPS,5,'<linear_attenuation>'+str(att_by_distance)+'</linear_attenuation>')
+ self.writel(S_LAMPS,5,'<falloff_angle>'+str(math.degrees(light.spot_size))+'</falloff_angle>')
+ self.writel(S_LAMPS,4,'</spot>')
+
+
+ else: #write a sun lamp for everything else (not supported)
+ self.writel(S_LAMPS,4,'<directional>')
+ self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>')
+ self.writel(S_LAMPS,4,'</directional>')
+
+
+ self.writel(S_LAMPS,3,'</technique_common>')
+ self.writel(S_LAMPS,2,'</optics>')
+ self.writel(S_LAMPS,1,'</light>')
+
+
+ self.writel(S_NODES,il,'<instance_light url="#'+lightid+'"/>')
+
+
+ def export_curve(self,curve):
+
+ splineid = self.new_id("spline")
+
+ self.writel(S_GEOM,1,'<geometry id="'+splineid+'" name="'+curve.name+'">')
+ self.writel(S_GEOM,2,'<spline closed="0">')
+
+ points=[]
+ interps=[]
+ handles_in=[]
+ handles_out=[]
+ tilts=[]
+
+ for cs in curve.splines:
+
+ if (cs.type=="BEZIER"):
+ for s in cs.bezier_points:
+ points.append(s.co[0])
+ points.append(s.co[1])
+ points.append(s.co[2])
+
+
+ handles_in.append(s.handle_left[0])
+ handles_in.append(s.handle_left[1])
+ handles_in.append(s.handle_left[2])
+
+ handles_out.append(s.handle_right[0])
+ handles_out.append(s.handle_right[1])
+ handles_out.append(s.handle_right[2])
+
+
+ tilts.append(s.tilt)
+ interps.append("BEZIER")
+ else:
+
+ for s in cs.points:
+ points.append(s.co[0])
+ points.append(s.co[1])
+ points.append(s.co[2])
+ handles_in.append(s.co[0])
+ handles_in.append(s.co[1])
+ handles_in.append(s.co[2])
+ handles_out.append(s.co[0])
+ handles_out.append(s.co[1])
+ handles_out.append(s.co[2])
+ tilts.append(s.tilt)
+ interps.append("LINEAR")
+
+
+
+
+ self.writel(S_GEOM,3,'<source id="'+splineid+'-positions">')
+ position_values=""
+ for x in points:
+ position_values+=" "+str(x)
+ self.writel(S_GEOM,4,'<float_array id="'+splineid+'-positions-array" count="'+str(len(points))+'">'+position_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-positions-array" count="'+str(len(points)/3)+'" stride="3">')
+ self.writel(S_GEOM,5,'<param name="X" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,3,'</source>')
+
+ self.writel(S_GEOM,3,'<source id="'+splineid+'-intangents">')
+ intangent_values=""
+ for x in handles_in:
+ intangent_values+=" "+str(x)
+ self.writel(S_GEOM,4,'<float_array id="'+splineid+'-intangents-array" count="'+str(len(points))+'">'+intangent_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-intangents-array" count="'+str(len(points)/3)+'" stride="3">')
+ self.writel(S_GEOM,5,'<param name="X" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,3,'</source>')
+
+ self.writel(S_GEOM,3,'<source id="'+splineid+'-outtangents">')
+ outtangent_values=""
+ for x in handles_out:
+ outtangent_values+=" "+str(x)
+ self.writel(S_GEOM,4,'<float_array id="'+splineid+'-outtangents-array" count="'+str(len(points))+'">'+outtangent_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-outtangents-array" count="'+str(len(points)/3)+'" stride="3">')
+ self.writel(S_GEOM,5,'<param name="X" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
+ self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,3,'</source>')
+
+ self.writel(S_GEOM,3,'<source id="'+splineid+'-interpolations">')
+ interpolation_values=""
+ for x in interps:
+ interpolation_values+=" "+x
+ self.writel(S_GEOM,4,'<Name_array id="'+splineid+'-interpolations-array" count="'+str(len(interps))+'">'+interpolation_values+'</Name_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-interpolations-array" count="'+str(len(interps))+'" stride="1">')
+ self.writel(S_GEOM,5,'<param name="INTERPOLATION" type="name"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,3,'</source>')
+
+
+ self.writel(S_GEOM,3,'<source id="'+splineid+'-tilts">')
+ tilt_values=""
+ for x in tilts:
+ tilt_values+=" "+str(x)
+ self.writel(S_GEOM,4,'<float_array id="'+splineid+'-tilts-array" count="'+str(len(tilts))+'">'+tilt_values+'</float_array>')
+ self.writel(S_GEOM,4,'<technique_common>')
+ self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-tilts-array" count="'+str(len(tilts))+'" stride="1">')
+ self.writel(S_GEOM,5,'<param name="TILT" type="float"/>')
+ self.writel(S_GEOM,4,'</accessor>')
+ self.writel(S_GEOM,3,'</source>')
+
+ self.writel(S_GEOM,3,'<control_vertices>')
+ self.writel(S_GEOM,4,'<input semantic="POSITION" source="#'+splineid+'-positions"/>')
+ self.writel(S_GEOM,4,'<input semantic="IN_TANGENT" source="#'+splineid+'-intangents"/>')
+ self.writel(S_GEOM,4,'<input semantic="OUT_TANGENT" source="#'+splineid+'-outtangents"/>')
+ self.writel(S_GEOM,4,'<input semantic="INTERPOLATION" source="#'+splineid+'-interpolations"/>')
+ self.writel(S_GEOM,4,'<input semantic="TILT" source="#'+splineid+'-tilts"/>')
+ self.writel(S_GEOM,3,'</control_vertices>')
+
+
+ self.writel(S_GEOM,2,'</spline>')
+ self.writel(S_GEOM,1,'</geometry>')
+
+ return splineid
+
+ def export_curve_node(self,node,il):
+
+ if (node.data==None):
+ return
+ curveid = self.export_curve(node.data)
+
+ self.writel(S_NODES,il,'<instance_geometry url="#'+curveid+'">')
+ self.writel(S_NODES,il,'</instance_geometry>')
+
+
+
+ def export_node(self,node,il):
+
+ if (not self.is_node_valid(node)):
+ return
+
+ self.writel(S_NODES,il,'<node id="'+self.validate_id(node.name)+'" name="'+node.name+'" type="NODE">')
+ il+=1
+
+ self.writel(S_NODES,il,'<matrix sid="transform">'+strmtx(node.matrix_local)+'</matrix>')
+ print("NODE TYPE: "+node.type+" NAME: "+node.name)
+ if (node.type=="MESH"):
+ self.export_mesh_node(node,il)
+ elif (node.type=="CURVE"):
+ self.export_curve_node(node,il)
+ elif (node.type=="ARMATURE"):
+ self.export_armature_node(node,il)
+ elif (node.type=="CAMERA"):
+ self.export_camera_node(node,il)
+ elif (node.type=="LAMP"):
+ self.export_lamp_node(node,il)
+
+ self.valid_nodes.append(node)
+ for x in node.children:
+ self.export_node(x,il)
+ il-=1
+ self.writel(S_NODES,il,'</node>')
+
+ def is_node_valid(self,node):
+ if (not node.type in self.config["object_types"]):
+ return False
+ if (self.config["use_active_layers"]):
+ valid=False
+ for i in range(20):
+ if (node.layers[i] and self.scene.layers[i]):
+ valid=True
+ break
+ if (not valid):
+ return False
+
+ if (self.config["use_export_selected"] and not node.select):
+ return False
+
+ return True
+
+
+ def export_scene(self):
+
+
+ self.writel(S_NODES,0,'<library_visual_scenes>')
+ self.writel(S_NODES,1,'<visual_scene id="'+self.scene_name+'" name="scene">')
+
+ for obj in self.scene.objects:
+ if (obj.parent==None):
+ self.export_node(obj,2)
+
+ self.writel(S_NODES,1,'</visual_scene>')
+ self.writel(S_NODES,0,'</library_visual_scenes>')
+
+ def export_asset(self):
+
+
+ self.writel(S_ASSET,0,'<asset>')
+ # Why is this time stuff mandatory?, no one could care less...
+ self.writel(S_ASSET,1,'<contributor>')
+ self.writel(S_ASSET,2,'<author> Anonymous </author>') #Who made Collada, the FBI ?
+ self.writel(S_ASSET,2,'<authoring_tool> Collada Exporter for Blender 2.6+, by Juan Linietsky (juan@codenix.com) </authoring_tool>') #Who made Collada, the FBI ?
+ self.writel(S_ASSET,1,'</contributor>')
+ self.writel(S_ASSET,1,'<created>'+time.strftime("%Y-%m-%dT%H:%M:%SZ ")+'</created>')
+ self.writel(S_ASSET,1,'<modified>'+time.strftime("%Y-%m-%dT%H:%M:%SZ")+'</modified>')
+ self.writel(S_ASSET,1,'<unit meter="1.0" name="meter"/>')
+ self.writel(S_ASSET,1,'<up_axis>Z_UP</up_axis>')
+ self.writel(S_ASSET,0,'</asset>')
+
+
+ def export_animation_transform_channel(self,target,transform_keys):
+
+ frame_total=len(transform_keys)
+ anim_id=self.new_id("anim")
+ self.writel(S_ANIM,1,'<animation id="'+anim_id+'">')
+ source_frames = ""
+ source_transforms = ""
+ source_interps = ""
+
+ for k in transform_keys:
+ source_frames += " "+str(k[0])
+ source_transforms += " "+strmtx(k[1])
+ source_interps +=" LINEAR"
+
+
+ # Time Source
+ self.writel(S_ANIM,2,'<source id="'+anim_id+'-input">')
+ self.writel(S_ANIM,3,'<float_array id="'+anim_id+'-input-array" count="'+str(frame_total)+'">'+source_frames+'</float_array>')
+ self.writel(S_ANIM,3,'<technique_common>')
+ self.writel(S_ANIM,4,'<accessor source="'+anim_id+'-input-array" count="'+str(frame_total)+'" stride="1">')
+ self.writel(S_ANIM,5,'<param name="TIME" type="float"/>')
+ self.writel(S_ANIM,4,'</accessor>')
+ self.writel(S_ANIM,3,'</technique_common>')
+ self.writel(S_ANIM,2,'</source>')
+
+ # Transform Source
+ self.writel(S_ANIM,2,'<source id="'+anim_id+'-transform-output">')
+ self.writel(S_ANIM,3,'<float_array id="'+anim_id+'-transform-output-array" count="'+str(frame_total*16)+'">'+source_transforms+'</float_array>')
+ self.writel(S_ANIM,3,'<technique_common>')
+ self.writel(S_ANIM,4,'<accessor source="'+anim_id+'-transform-output-array" count="'+str(frame_total)+'" stride="16">')
+ self.writel(S_ANIM,5,'<param name="TRANSFORM" type="float4x4"/>')
+ self.writel(S_ANIM,4,'</accessor>')
+ self.writel(S_ANIM,3,'</technique_common>')
+ self.writel(S_ANIM,2,'</source>')
+
+ # Interpolation Source
+ self.writel(S_ANIM,2,'<source id="'+anim_id+'-interpolation-output">')
+ self.writel(S_ANIM,3,'<Name_array id="'+anim_id+'-interpolation-output-array" count="'+str(frame_total)+'">'+source_interps+'</Name_array>')
+ self.writel(S_ANIM,3,'<technique_common>')
+ self.writel(S_ANIM,4,'<accessor source="'+anim_id+'-interpolation-output-array" count="'+str(frame_total)+'" stride="1">')
+ self.writel(S_ANIM,5,'<param name="INTERPOLATION" type="Name"/>')
+ self.writel(S_ANIM,4,'</accessor>')
+ self.writel(S_ANIM,3,'</technique_common>')
+ self.writel(S_ANIM,2,'</source>')
+
+ self.writel(S_ANIM,2,'<sampler id="'+anim_id+'-sampler">')
+ self.writel(S_ANIM,3,'<input semantic="INPUT" source="#'+anim_id+'-input"/>')
+ self.writel(S_ANIM,3,'<input semantic="OUTPUT" source="#'+anim_id+'-transform-output"/>')
+ self.writel(S_ANIM,3,'<input semantic="INTERPOLATION" source="#'+anim_id+'-interpolation-output"/>')
+ self.writel(S_ANIM,2,'</sampler>')
+ self.writel(S_ANIM,2,'<channel source="#'+anim_id+'-sampler" target="'+target+'/transform"/>')
+ self.writel(S_ANIM,1,'</animation>')
+
+ return [anim_id]
+
+
+ def export_animation(self,start,end):
+
+ #Blender -> Collada frames needs a little work
+ #Collada starts from 0, blender usually from 1
+ #The last frame must be included also
+
+ frame_len = 1.0 / self.scene.render.fps
+ frame_total = end - start + 1
+ frame_sub = 0
+ if (start>0):
+ frame_sub=start*frame_len
+
+ tcn = []
+ xform_cache={}
+ # Change frames first, export objects last
+ # This improves performance enormously
+
+ print("anim from: "+str(start)+" to "+str(end))
+ for t in range(start,end+1):
+ self.scene.frame_set(t)
+ key = t * frame_len - frame_sub
+# print("Export Anim Frame "+str(t)+"/"+str(self.scene.frame_end+1))
+
+ for node in self.scene.objects:
+
+ if (not node in self.valid_nodes):
+ continue
+
+ if (node.type=="MESH" and node.parent and node.parent.type=="ARMATURE"):
+ continue #In Collada, nodes that have skin modifier must not export animation, animate the skin instead.
+
+ if (len(node.constraints)>0 or node.animation_data!=None):
+ #If the node has constraints, or animation data, then export a sampled animation track
+ name=self.validate_id(node.name)
+ if (not (name in xform_cache)):
+ xform_cache[name]=[]
+
+ mtx = node.matrix_world.copy()
+ if (node.parent):
+ mtx = node.parent.matrix_world.inverted() * mtx
+
+ xform_cache[name].append( (key,mtx) )
+
+ if (node.type=="ARMATURE"):
+ #All bones exported for now
+ for bone in node.data.bones:
+
+ bone_name=self.skeleton_info[node]["bone_ids"][bone]
+
+ if (not (bone_name in xform_cache)):
+ xform_cache[bone_name]=[]
+
+ posebone = node.pose.bones[bone.name]
+ parent_posebone=None
+
+ mtx = posebone.matrix.copy()
+ if (bone.parent):
+ parent_posebone=node.pose.bones[bone.parent.name]
+ mtx = parent_posebone.matrix.inverted() * mtx
+
+
+ xform_cache[bone_name].append( (key,mtx) )
+
+
+ #export animation xml
+ for nid in xform_cache:
+ tcn+=self.export_animation_transform_channel(nid,xform_cache[nid])
+
+ return tcn
+
+ def export_animations(self):
+
+ self.writel(S_ANIM,0,'<library_animations>')
+
+
+
+ if (self.config["use_anim_action_all"] and len(self.skeletons)):
+
+ self.writel(S_ANIM_CLIPS,0,'<library_animation_clips>')
+
+ for x in bpy.data.actions[:]:
+ if x in self.action_constraints:
+ continue
+ for y in self.skeletons:
+ if (y.animation_data):
+ y.animation_data.action=x;
+
+
+ tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]))
+ framelen=(1.0/self.scene.render.fps)
+ start = x.frame_range[0]*framelen
+ end = x.frame_range[1]*framelen
+ print("Export anim: "+x.name)
+ self.writel(S_ANIM_CLIPS,1,'<animation_clip name="'+x.name+'" start="'+str(start)+'" end="'+str(end)+'">')
+ for z in tcn:
+ self.writel(S_ANIM_CLIPS,2,'<instance_animation url="#'+z+'">')
+ self.writel(S_ANIM_CLIPS,1,'</animation_clip>')
+
+
+ self.writel(S_ANIM_CLIPS,0,'</library_animation_clips>')
+
+ else:
+ self.export_animation(self.scene.frame_start,self.scene.frame_end)
+
+ self.writel(S_ANIM,0,'</library_animations>')
+
+ def export(self):
+
+ self.writel(S_GEOM,0,'<library_geometries>')
+ self.writel(S_CONT,0,'<library_controllers>')
+ self.writel(S_CAMS,0,'<library_cameras>')
+ self.writel(S_LAMPS,0,'<library_lights>')
+ self.writel(S_IMGS,0,'<library_images>')
+ self.writel(S_MATS,0,'<library_materials>')
+ self.writel(S_FX,0,'<library_effects>')
+
+
+ self.skeletons=[]
+ self.action_constraints=[]
+ self.export_asset()
+ self.export_scene()
+
+ self.writel(S_GEOM,0,'</library_geometries>')
+ self.writel(S_CONT,0,'</library_controllers>')
+ self.writel(S_CAMS,0,'</library_cameras>')
+ self.writel(S_LAMPS,0,'</library_lights>')
+ self.writel(S_IMGS,0,'</library_images>')
+ self.writel(S_MATS,0,'</library_materials>')
+ self.writel(S_FX,0,'</library_effects>')
+
+ if (self.config["use_anim"]):
+ self.export_animations()
+
+ try:
+ f = open(self.path,"wb")
+ except:
+ return False
+
+ f.write(bytes('<?xml version="1.0" encoding="utf-8"?>\n',"UTF-8"))
+ f.write(bytes('<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">\n',"UTF-8"))
+
+
+ s=[]
+ for x in self.sections.keys():
+ s.append(x)
+ s.sort()
+ for x in s:
+ for l in self.sections[x]:
+ f.write(bytes(l+"\n","UTF-8"))
+
+ f.write(bytes('<scene>\n',"UTF-8"))
+ f.write(bytes('\t<instance_visual_scene url="#'+self.scene_name+'" />\n',"UTF-8"))
+ f.write(bytes('</scene>\n',"UTF-8"))
+ f.write(bytes('</COLLADA>\n',"UTF-8"))
+ return True
+
+ def __init__(self,path,kwargs):
+ self.scene=bpy.context.scene
+ self.last_id=0
+ self.scene_name=self.new_id("scene")
+ self.sections={}
+ self.path=path
+ self.mesh_cache={}
+ self.curve_cache={}
+ self.material_cache={}
+ self.image_cache={}
+ self.skeleton_info={}
+ self.config=kwargs
+ self.valid_nodes=[]
+
+
+
+
+def save(operator, context,
+ filepath="",
+ use_selection=False,
+ **kwargs
+ ):
+
+ exp = DaeExporter(filepath,kwargs)
+ exp.export()
+
+ return {'FINISHED'} # so the script wont run after we have batch exported.
+
+
diff --git a/tools/export/export_lua.py b/tools/export/export_lua.py
new file mode 100644
index 0000000000..6f9bafd611
--- /dev/null
+++ b/tools/export/export_lua.py
@@ -0,0 +1,209 @@
+
+def tw(f,t,st):
+ for x in range(t):
+ f.write("\t")
+ nl = True
+ if st[-1] == "#":
+ nl = False
+ st = st[:-1]
+ f.write(st)
+ if nl:
+ f.write("\n")
+
+
+def write_property_lua(f, tab, name, value, pref = ""):
+
+ tw(f, tab, '%s{ name = "%s",' % (pref, name))
+ tab = tab + 1
+
+ if (type(value)==str):
+
+ tw(f, tab, 'value = "%s",' % value)
+ tw(f, t, 'type = "string",')
+
+ elif (type(value)==bool):
+
+
+ if (value):
+ tw(f, tab, 'value = true,')
+ else:
+ tw(f, tab, 'value = false,')
+
+ tw(f, t, 'type = "bool",')
+
+ elif (type(value)==int):
+
+ tw(f, t, 'type = "int",')
+ tw(f, tab, 'value = %d,' % value)
+
+ elif (type(value)==float):
+
+ tw(f, t, 'type = "real",')
+ tw(f, tab, 'value = %f,' % value)
+
+ elif (type(value)==dict):
+
+ tw(f, t, 'type = "dictionary",')
+ for x in value:
+ write_property_lua(f,tab,x,value[x])
+
+ elif (isinstance(value,ObjectTree)):
+ if (not value._resource):
+ print("ERROR: Not a resource!!")
+ tw(f, tab-1, "},")
+ return
+
+ tw(f, tab, 'type = "resource",')
+ tw(f, tab, 'resource_type = "%s",' % value._type)
+
+ if (value._res_path!=""):
+
+ tw(f, tab, 'path = "%s",' % value._res_path)
+
+ else:
+
+ tw(f, tab, "value = {")
+ tab = tab + 1
+ tw(f, tab, 'type = "%s",' % value._type)
+
+ for x in value._properties:
+ write_property_lua(f,tab,x[0],x[1])
+
+ tab = tab - 1
+ tw(f, tab, "},")
+
+ elif (isinstance(value,Color)):
+
+ tw(f, tab, 'type = "color",')
+ tw(f, tab, 'value = { %.20f, %.20f, %.20f, %.20f },' % (value.r, value.g, value.b, value.a))
+
+ elif (isinstance(value,Vector3)):
+
+ tw(f, tab, 'type = "vector3",')
+ tw(f, tab, 'value = { %.20f, %.20f, %.20f },' % (value.x, value.y, value.z))
+
+ elif (isinstance(value,Quat)):
+
+ tw(f, tab, 'type = "quaternion",')
+ tw(f, tab, 'value = { %.20f, %.20f, %.20f, %.20f },' % (value.x, value.y, value.z, value.w))
+
+ elif (isinstance(value,Matrix4x3)): # wtf, blender matrix?
+
+ tw(f, tab, 'type = "transform",')
+ tw(f, tab, 'value = { #')
+ for i in range(3):
+ for j in range(3):
+ f.write("%.20f, " % value.m[j][i])
+
+ for i in range(3):
+ f.write("%.20f, " % value.m[i][3])
+
+ f.write("},\n")
+
+ elif (type(value)==list):
+ if (len(value)==0):
+ tw(f, tab-1, "},")
+ return
+ first=value[0]
+ if (type(first)==int):
+
+ tw(f, tab, 'type = "int_array",')
+ tw(f, tab, 'value = #')
+ for i in range(len(value)):
+ f.write("%d, " % value[i])
+ f.write("},\n")
+
+ elif (type(first)==float):
+
+ tw(f, tab, 'type = "real_array",')
+ tw(f, tab, 'value = #')
+ for i in range(len(value)):
+ f.write("%.20f, " % value[i])
+ f.write("},\n")
+
+
+ elif (type(first)==str):
+
+ tw(f, tab, 'type = "string_array",')
+ tw(f, tab, 'value = #')
+ for i in range(len(value)):
+ f.write('"%s", ' % value[i])
+ f.write("},\n")
+
+ elif (isinstance(first,Vector3)):
+
+ tw(f, tab, 'type = "vector3_array",')
+ tw(f, tab, 'value = #')
+ for i in range(len(value)):
+ f.write("{ %.20f, %.20f, %.20f }, " % (value[i].x, value[i].y, value[i].z))
+ f.write("},\n")
+
+ elif (isinstance(first,Color)):
+
+ tw(f, tab, 'type = "color_array",')
+ tw(f, tab, 'value = #')
+ for i in range(len(value)):
+ f.write("{ %.20f, %.20f, %.20f, %.20f }, " % (value[i].r, value[i].g, value[i].b, value[i].a))
+ f.write("},\n")
+
+ elif (type(first)==dict):
+
+ tw(f, tab, 'type = "dict_array",')
+ tw(f, tab, 'value = {')
+
+ for i in range(len(value)):
+ write_property_lua(f,tab+1,str(i+1),value[i])
+
+ tw(f, tab, '},')
+
+
+ tw(f, tab-1, "},")
+
+
+
+def write_node_lua(f,tab,tree,path):
+
+ tw(f, tab, '{ type = "%s",')
+
+ if not tree._resource:
+ tw(f, tab+1, 'meta = {')
+ write_property_lua(f, tab+3, "name", tree._name)
+ if path != "":
+ write_property_lua(f, tab+3, "path", path)
+ tw(f, tab+1, '},')
+
+ tw(f, tab+1, "properties = {,")
+ for x in tree._properties:
+ write_property_lua(f,tab+2,x[0],x[1])
+ tw(f, tab+1, "},")
+
+ tw(f, tab, '},')
+
+
+ if (path==""):
+ path="."
+ else:
+ if (path=="."):
+ path=tree._name
+ else:
+ path=path+"/"+tree._name
+ #path="."
+ for x in tree._children:
+ write_node_lua(f,tab,x,path)
+
+def write(tree,fname):
+ f=open(fname,"wb")
+ f.write("return = {\n")
+
+ f.write('\tmagic = "SCENE",\n')
+ tab = 1
+
+ write_node_lua(f,tab,tree,"")
+
+ f.write("}\n\n")
+
+
+
+
+
+
diff --git a/tools/export/godot_export.py b/tools/export/godot_export.py
new file mode 100644
index 0000000000..0503fa6521
--- /dev/null
+++ b/tools/export/godot_export.py
@@ -0,0 +1,1842 @@
+#!BPY
+# -*- coding: utf-8 -*-
+"""
+Name: 'godot export (.xml)...'
+Blender: 241
+Group: 'Export'
+Tooltip: 'Godot exporter'
+"""
+
+
+godot_revision="$Rev: 2068 $"
+
+VERSION = "1.0"
+
+import os
+import Blender
+import math
+from Blender.BGL import *
+
+MAX_WEIGHTS_PER_VERTEX = 4
+
+class ExporterData:
+ def __init__(self, fname):
+
+ self.resource_list=[]
+ self.mesh_caches={}
+ self.material_caches={}
+ self.filename = fname
+
+class ObjectTree:
+
+
+ def add(self,p_prop,p_val):
+ self._properties+=[(p_prop,p_val)]
+
+ def __init__(self,p_parent,p_type,p_name=""):
+ self._parent=p_parent
+ self._name=p_name
+ self._type=p_type
+ self._properties=[]
+ self._children=[]
+ self._resource=False
+ self._res_path=""
+ self._bone_map=None
+
+
+def get_root_objects(scene):
+ objs=[]
+ for x in list(scene.objects):
+ parent = x.getParent()
+ if (parent==None):
+ objs+=[x]
+ return objs
+
+def get_children_objects(scene,node):
+ objs=[]
+ for x in list(scene.objects):
+ if (x.getParent()==None or x.getParent().getName()!=node.getName()):
+ continue
+ objs+=[x]
+
+ return objs
+
+
+def convert_matrix(m):
+
+ mat = m.copy()
+
+
+# Invert Z by Y, including position, but leave [2][2] alone, which is done by mirroring
+#
+
+ for col in range(4):
+ tmp = mat[col][1]
+ mat[col][1] = mat[col][2]
+ mat[col][2] = tmp
+
+ for row in range(4):
+ tmp = mat[1][row]
+ mat[1][row] = mat[2][row]
+ mat[2][row] = tmp
+
+
+ mat[2][0]=-mat[2][0]
+ mat[2][1]=-mat[2][1]
+ mat[2][3]=-mat[2][3]
+ mat[0][2]=-mat[0][2]
+ mat[1][2]=-mat[1][2]
+ mat[3][2]=-mat[3][2]
+
+ return mat
+
+
+def eq_vec(a,b):
+ return (a.distance_to(b)<0.0001)
+
+def eq_uv(a,b):
+ return (a.distance_to(b)<0.0001)
+
+def add_vec(a,b):
+ return Vector3( (a.x+b.x, a.y+b.y, a.z+b.z ) )
+
+def sub_vec(a,b):
+ return Vector3( (a.x-b.x, a.y-b.y, a.z-b.z ) )
+
+def mul_vec(a,b):
+ return Vector3( (a.x*b.x, a.y*b.y, a.z*b.z ) )
+
+def dot_vec(a,b):
+ return a.x*b.x + a.y*b.y + a.z*b.z
+
+def cross_vec(a,b):
+ x = (a.y * b.z) - (a.z * b.y);
+ y = (a.z * b.x) - (a.x * b.z);
+ z = (a.x * b.y) - (a.y * b.x);
+ return Vector3( (x,y,z) )
+
+def mul_vecs(a,s):
+ return Vector3( (a.x*s, a.y*s, a.z*s) )
+
+
+def div_vecs(a,s):
+ return Vector3( (a.x/s, a.y/s, a.z/s) )
+
+
+class Color:
+ def average(self):
+ return (self.r+self.g+self.b)/3.0
+
+ def __init__(self,tup):
+
+ self.r=0
+ self.g=0
+ self.b=0
+ self.a=1.0
+
+ if (len(tup)>=1):
+ self.r=tup[0]
+ if (len(tup)>=2):
+ self.g=tup[1]
+ if (len(tup)>=3):
+ self.b=tup[2]
+ if (len(tup)>=4):
+ self.a=tup[3]
+
+
+
+
+class Vector3:
+
+ def distance_to(self,v):
+ return math.sqrt( (self.x-v.x)**2 + (self.y-v.y)**2 + (self.z-v.z)**2 );
+ def length(self):
+ return math.sqrt( self.x**2 + self.y**2 + self.z**2 )
+ def normalize(self):
+ l=self.length()
+ if (l==0.0):
+ return
+ self.x/=l
+ self.y/=l
+ self.z/=l
+
+ def __init__(self,tup):
+ self.x=0
+ self.y=0
+ self.z=0
+
+ if (len(tup)>=1):
+ self.x=tup[0]
+ if (len(tup)>=2):
+ self.y=tup[1]
+ if (len(tup)>=3):
+ self.z=tup[2]
+
+
+class Matrix4x3:
+
+ def invert(self):
+
+ self.m[0][1], self.m[1][0]=self.m[1][0], self.m[0][1]
+ self.m[0][2], self.m[2][0]=self.m[2][0], self.m[0][2]
+ self.m[1][2], self.m[2][1]=self.m[2][1], self.m[1][2]
+
+ x= -self.m[0][3];
+ y= -self.m[1][3];
+ z= -self.m[2][3];
+
+ self.m[0][3]= (self.m[0][0]*x ) + ( self.m[1][0]*y ) + ( self.m[2][0]*z );
+ self.m[1][3]= (self.m[0][1]*x ) + ( self.m[1][1]*y ) + ( self.m[2][1]*z );
+ self.m[2][3]= (self.m[0][2]*x ) + ( self.m[1][2]*y ) + ( self.m[2][2]*z );
+
+ def mult_by(self,mat):
+
+ new_m=Matrix4x3()
+ for j in range(4):
+ for i in range(3):
+ ab = 0;
+ for k in range(3):
+ ab += self.m[i][k] * mat.m[k][j];
+
+ new_m.m[i][j]=ab;
+ self.m=new_m.m
+ """
+ def mult_by(mat):
+ res=Matrix4x3()
+ res.elements[0][0] =solf.m[0][0] * self.m[0][0] +solf.m[0][1] * self.m[1][0] +solf.m[0][2] * self.m[2][0];
+ res.elements[0][1] =solf.m[0][0] * self.m[0][1] +solf.m[0][1] * self.m[1][1] +solf.m[0][2] * self.m[2][1];
+ res.elements[0][2] =solf.m[0][0] * self.m[0][2] +solf.m[0][1] * self.m[1][2] +solf.m[0][2] * self.m[2][2];
+
+ res.elements[1][0] =solf.m[1][0] * self.m[0][0] +solf.m[1][1] * self.m[1][0] +solf.m[1][2] * self.m[2][0];
+ res.elements[1][1] =solf.m[1][0] * self.m[0][1] +solf.m[1][1] * self.m[1][1] +solf.m[1][2] * self.m[2][1];
+ res.elements[1][2] =solf.m[1][0] * self.m[0][2] +solf.m[1][1] * self.m[1][2] +solf.m[1][2] * self.m[2][2];
+
+ res.elements[2][0] =solf.m[2][0] * self.m[0][0] +solf.m[2][1] * self.m[1][0] +solf.m[2][2] * self.m[2][0];
+ res.elements[2][1] =solf.m[2][0] * self.m[0][1] +solf.m[2][1] * self.m[1][1] +solf.m[2][2] * self.m[2][1];
+ res.elements[2][2] =solf.m[2][0] * self.m[0][2] +solf.m[2][1] * self.m[1][2] +solf.m[2][2] * self.m[2][2];
+ """
+ def xform(self,vec):
+
+ x=self.m[0][0] * vec.x + self.m[0][1] * vec.y + self.m[0][2] * vec.z + self.m[0][3]
+ y=self.m[1][0] * vec.x + self.m[1][1] * vec.y + self.m[1][2] * vec.z + self.m[1][3]
+ z=self.m[2][0] * vec.x + self.m[2][1] * vec.y + self.m[2][2] * vec.z + self.m[2][3]
+ return Vector3( (x,y,z ) )
+
+ def xform_basis(self,vec):
+
+ x=self.m[0][0] * vec.x + self.m[0][1] * vec.y + self.m[0][2] * vec.z
+ y=self.m[1][0] * vec.x + self.m[1][1] * vec.y + self.m[1][2] * vec.z
+ z=self.m[2][0] * vec.x + self.m[2][1] * vec.y + self.m[2][2] * vec.z
+ return Vector3( (x,y,z ) )
+
+ def copy(self):
+ ret=Matrix4x3();
+ for i in range(3):
+ for j in range(4):
+ ret.m[i][j]=self.m[i][j]
+ return ret;
+
+ def setBlenderMatrix(self,bm):
+ for i in range(3):
+ for j in range(3):
+ self.m[i][j]=bm[i][j]
+
+ self.m[i][3]=bm[3][i] #weird
+
+ def getBlenderMatrix(self):
+ bm=Blender.Mathutils.Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,1])
+ for i in range(3):
+ for j in range(3):
+ bm[i][j]=self.m[i][j]
+
+ bm[3][i]=self.m[i][3] #weird
+
+ return bm;
+
+ def getPos(self):
+ return Vector3( (self.m[0][3], self.m[1][3], self.m[2][3]) )
+ def getScale(self):
+ norm=((\
+ Vector3((self.m[0][0], self.m[0][1], self.m[0][2])).length(),\
+ Vector3((self.m[1][0], self.m[1][1], self.m[1][2])).length(),\
+ Vector3((self.m[2][0], self.m[2][1], self.m[2][2])).length()\
+ ))
+ return Vector3(norm)
+
+ def scale(self,s):
+ self.m[0][0]*=s.x;
+ self.m[0][1]*=s.x;
+ self.m[0][2]*=s.x;
+ self.m[0][3]*=s.x;
+ self.m[1][0]*=s.y;
+ self.m[1][1]*=s.y;
+ self.m[1][2]*=s.y;
+ self.m[1][3]*=s.y;
+ self.m[2][0]*=s.z;
+ self.m[2][1]*=s.z;
+ self.m[2][2]*=s.z;
+ self.m[2][3]*=s.z;
+
+ def scale3x3(self,s):
+ self.m[0][0]*=s.x;
+ self.m[0][1]*=s.x;
+ self.m[0][2]*=s.x;
+ self.m[1][0]*=s.y;
+ self.m[1][1]*=s.y;
+ self.m[1][2]*=s.y;
+ self.m[2][0]*=s.z;
+ self.m[2][1]*=s.z;
+ self.m[2][2]*=s.z;
+
+ def clearScale(self):
+ s=self.getScale();
+ s.x=1.0/s.x
+ s.y=1.0/s.y
+ s.z=1.0/s.z
+ self.scale3x3(s)
+ def set_rotation( self, p_axis, p_phi ):
+ axis_sq = Vector3([p_axis.x*p_axis.x,p_axis.y*p_axis.y,p_axis.z*p_axis.z])
+
+ cosine= math.cos(p_phi);
+ sine= math.sin(p_phi);
+
+ self.m[0][0] = axis_sq.x + cosine * ( 1.0 - axis_sq.x );
+ self.m[0][1] = p_axis.x * p_axis.y * ( 1.0 - cosine ) + p_axis.z * sine;
+ self.m[0][2] = p_axis.z * p_axis.x * ( 1.0 - cosine ) - p_axis.y * sine;
+
+ self.m[1][0] = p_axis.x * p_axis.y * ( 1.0 - cosine ) - p_axis.z * sine;
+ self.m[1][1] = axis_sq.y + cosine * ( 1.0 - axis_sq.y );
+ self.m[1][2] = p_axis.y * p_axis.z * ( 1.0 - cosine ) + p_axis.x * sine;
+
+ self.m[2][0] = p_axis.z * p_axis.x * ( 1.0 - cosine ) + p_axis.y * sine;
+ self.m[2][1] = p_axis.y * p_axis.z * ( 1.0 - cosine ) - p_axis.x * sine;
+ self.m[2][2] = axis_sq.z + cosine * ( 1.0 - axis_sq.z );
+
+ def __init__(self):
+ self.m=[[1,0,0,0],[0,1,0,0],[0,0,1,0]]
+
+
+class Quat:
+
+ def distance_to(self,v):
+ return math.sqrt( (self.x-v.x)**2 + (self.y-v.y)**2 + (self.z-v.z)**2+ (self.w-v.w)**2 );
+
+ def __init__(self,p_mat):
+ """
+ q=mat.getBlenderMatrix().toQuat();
+ self.x=q.x;
+ self.y=q.y;
+ self.z=q.z;
+ self.w=q.w;
+ """
+
+ mat=p_mat.copy()
+#create quaternion from 4x3 matrix
+
+ trace = mat.m[0][0] + mat.m[1][1] + mat.m[2][2];
+ temp=[0,0,0,0];
+
+ if (trace > 0) :
+
+ s =math.sqrt(trace + 1.0);
+ temp[3]=(s * 0.5);
+ s = 0.5 / s;
+
+ temp[0]=((mat.m[2][1] - mat.m[1][2]) * s);
+ temp[1]=((mat.m[0][2] - mat.m[2][0]) * s);
+ temp[2]=((mat.m[1][0] - mat.m[0][1]) * s);
+
+ else :
+
+ i=int()
+ if (mat.m[0][0] < mat.m[1][1]):
+ if (mat.m[1][1] < mat.m[2][2]):
+ i=2
+ else:
+ i=1
+ else:
+ if (mat.m[0][0] < mat.m[2][2]):
+ i=2
+ else:
+ i=0
+
+ j = (i + 1) % 3;
+ k = (i + 2) % 3;
+
+ s = math.sqrt(mat.m[i][i] - mat.m[j][j] - mat.m[k][k] + 1.0);
+ temp[i] = s * 0.5;
+ s = 0.5 / s;
+
+ temp[3] = (mat.m[k][j] - mat.m[j][k]) * s;
+ temp[j] = (mat.m[j][i] + mat.m[i][j]) * s;
+ temp[k] = (mat.m[k][i] + mat.m[i][k]) * s;
+
+ self.x=temp[0]
+ self.y=temp[1]
+ self.z=temp[2]
+ self.w=temp[3]
+
+def snap_vec(vec):
+ ret=()
+ for x in vec:
+ ret+=( x-math.fmod(x,0.0001), )
+
+ return vec
+
+class Surface:
+
+
+ def write_to_res(self,res,i):
+ prep="surfaces/"+str(i)+"/"
+ format={}
+ format["primitive"]=4 # triangles
+ format["array_len"]=len(self._verts)
+ format["index_array_len"]=len(self._indices)
+
+ res.add(prep+"format",format)
+
+ if (self._material!=None):
+ res.add(prep+"material",self._material)
+ res.add(prep+"vertex_array",self._verts)
+ res.add(prep+"normal_array",self._normals)
+ res.add(prep+"index_array",self._indices)
+ format_str="vin"
+
+ if (len(self._tangents)):
+ res.add(prep+"tangent_array",self._tangents)
+ format_str+="t"
+
+ if (len(self._colors)):
+ res.add(prep+"color_array",self._colors)
+ format_str+="c"
+
+ if (len(self._uvs)):
+ res.add(prep+"tex_uv_array",self._uvs)
+ format_str+="u"
+
+ if (len(self._bone_indices)):
+ res.add(prep+"bone_array",self._bone_indices)
+ format_str+="b"
+
+ if (len(self._weights)):
+ res.add(prep+"weights_array",self._weights)
+ format_str+="w"
+
+ # binormals....
+ format["format"]=format_str
+
+# convert vertices to be compatile with Y_UP
+
+ def fix_vertex_axis(self,v):
+
+ return Vector3( (v.x, v.z, -v.y) );
+
+ def convert(self,applymatrix=None):
+
+ # STEP 1 fix coordinates
+ for i in range(len(self._verts)):
+ self._verts[i]=self.fix_vertex_axis(self._verts[i])
+ self._normals[i]=self.fix_vertex_axis(self._normals[i])
+ if (applymatrix):
+ for i in range(len(self._verts)):
+ self._verts[i]=applymatrix.xform( self._verts[i] )
+ self._normals[i]=applymatrix.xform_basis( self._normals[i] )
+
+ # STEP 2 fix indices
+
+ for i in range(len(self._indices)/3):
+ aux=self._indices[i*3+1]
+ self._indices[i*3+1]=self._indices[i*3+2]
+ self._indices[i*3+2]=aux
+
+ # STEP 4 compute binormals
+ if (len(self._uvs)):
+
+ tangents=[ Vector3( (0,0,0 ) ) ] * len(self._verts)
+ binormals=[ Vector3( (0,0,0 ) ) ] * len(self._verts)
+ for i in range(len(self._indices)/3):
+
+ v1 = self._verts[ self._indices[i*3+0] ]
+ v2 = self._verts[ self._indices[i*3+1] ]
+ v3 = self._verts[ self._indices[i*3+2] ]
+
+ w1 = self._uvs[ self._indices[i*3+0] ]
+ w2 = self._uvs[ self._indices[i*3+1] ]
+ w3 = self._uvs[ self._indices[i*3+2] ]
+
+
+ x1 = v2.x - v1.x
+ x2 = v3.x - v1.x
+ y1 = v2.y - v1.y
+ y2 = v3.y - v1.y
+ z1 = v2.z - v1.z
+ z2 = v3.z - v1.z
+
+ s1 = w2.x - w1.x
+ s2 = w3.x - w1.x
+ t1 = w2.y - w1.y
+ t2 = w3.y - w1.y
+
+ r = (s1 * t2 - s2 * t1);
+ if (r==0):
+ binormal=Vector3((0,0,0))
+ tangent=Vector3((0,0,0))
+ else:
+ tangent = Vector3(((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+ (t2 * z1 - t1 * z2) * r))
+ binormal = Vector3(((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+ (s1 * z2 - s2 * z1) * r))
+
+ tangents[ self._indices[i*3+0] ] = add_vec( tangents[ self._indices[i*3+0] ], tangent )
+ binormals[ self._indices[i*3+0] ] = add_vec( binormals[ self._indices[i*3+0] ], binormal )
+ tangents[ self._indices[i*3+1] ] = add_vec( tangents[ self._indices[i*3+1] ], tangent )
+ binormals[ self._indices[i*3+1] ] = add_vec( binormals[ self._indices[i*3+1] ], binormal )
+ tangents[ self._indices[i*3+2] ] = add_vec( tangents[ self._indices[i*3+2] ], tangent )
+ binormals[ self._indices[i*3+2] ] = add_vec( binormals[ self._indices[i*3+2] ], binormal )
+
+
+ for i in range(len(tangents)):
+
+ T = tangents[i]
+ T.normalize()
+ B = binormals[i]
+ B.normalize()
+ N=self._normals[i]
+ Tp = T #sub_vec( T, mul_vecs( N, dot_vec( N, T ) ) )
+ #Tp.normalize()
+ Bx = cross_vec( N, Tp )
+ if (dot_vec( Bx, B )<0):
+ Bw=-1.0
+ else:
+ Bw=1.0
+
+ self._tangents.append(float(Tp.x))
+ self._tangents.append(float(Tp.y))
+ self._tangents.append(float(Tp.z))
+ self._tangents.append(float(Bw))
+
+
+
+ def _insertVertex(self,face,i):
+
+ index_key=snap_vec((face.v[i].co.x,face.v[i].co.z,face.v[i].co.y))
+ v=Vector3(face.v[i].co)
+
+ if (face.smooth):
+ index_key+=snap_vec((face.v[i].no[0],face.v[i].no[1],face.v[i].no[2]))
+ else:
+ index_key+=snap_vec((face.no[0],face.no[1],face.no[2]))
+
+
+ uv=None
+ if (self._has_uv):
+ uv=face.uv[i]
+ uv=Vector3((uv[0],1.0-uv[1],0)) #save as vector3
+ index_key+=snap_vec((uv.x,uv.y))
+
+ index=-1
+ if (face.smooth and index_key in self._index_cache):
+ index=self._index_cache[index_key]
+
+
+ if (index==-1):
+#no similar vertex exists, so create a new one
+ self._verts+=[v]
+ if (face.smooth):
+ self._normals+=[Vector3(face.v[i].no)]
+ else:
+ self._normals+=[Vector3(face.no)]
+ if (self._has_uv):
+ self._uvs+=[uv]
+ if (self._has_color):
+ self._colors+=[Color((face.col[i].r/255.0,face.col[i].g/255.0,face.col[i].b/255.0,face.col[i].a/255.0))]
+
+ if (self._vertex_weights!=None):
+ for j in xrange(4):
+ self._bone_indices.append( self._vertex_weights[face.v[i].index][j*2+0] )
+ self._weights.append( self._vertex_weights[face.v[i].index][j*2+1] )
+
+ index=len(self._verts)-1
+ self._index_cache[index_key]=index
+
+ self._indices+=[index]
+
+
+
+
+
+
+ def insertFace(self,face):
+
+ if (len(face.v)>=3):
+ self._insertVertex(face,0)
+ self._insertVertex(face,1)
+ self._insertVertex(face,2)
+ if (len(face.v)>=4):
+ self._insertVertex(face,2)
+ self._insertVertex(face,3)
+ self._insertVertex(face,0)
+
+ def __init__(self):
+ self._mat=0
+ self._verts=[]
+ self._normals=[]
+ self._tangents=[]
+ self._colors=[]
+ self._indices=[]
+ self._uvs=[]
+ self._has_uv=False
+ self._bone_indices=[]
+ self._weights=[]
+ self._vertex_weights=[]
+ self._has_color=False
+ self._material=None
+ self._index_cache={}
+
+
+def make_material(mat,twosided_hint,exporter):
+
+
+ if (mat.getName() in exporter.material_caches):
+ # todo, find twosided and add it
+ #if (twosided_hint):
+ # material_caches[mat.getName()]._two_sided=True
+ return exporter.material_caches[mat.getName()]
+
+
+ print("doesn't have it")
+
+ res=ObjectTree(None,"FixedMaterial")
+ res._resource=True
+ res.add("resource/name",mat.getName())
+#color
+ diffuse_col = Color(mat.getRGBCol())
+ diffuse_col.a = 1.0 # mat.getAlpha() this doesn't work..
+ res.add("params/diffuse",diffuse_col)
+ spec_col = Color(mat.getSpecCol())
+ spec_col.r *= mat.getSpec()
+ spec_col.g *= mat.getSpec()
+ spec_col.b *= mat.getSpec()
+ res.add("params/specular",spec_col)
+
+ res.add("params/specular_exp",mat.getHardness())
+ res.add("params/emission",Color([mat.getEmit(),mat.getEmit(),mat.getEmit()]))
+#flags
+ res.add("flags/unshaded",bool(mat.getMode()&Blender.Material.Modes['SHADELESS']))
+ res.add("flags/wireframe",bool(mat.getMode()&Blender.Material.Modes['WIRE']))
+ res.add("flags/double_sided",bool(twosided_hint))
+#textures
+
+ have_primary=False
+ have_detail=False
+ detail_mix=1.0
+ default_diffuse = Color((1,1,1,1))
+ default_spec = Color((1,1,1,1))
+ gen_mode=0
+
+ for tx in mat.textures:
+ if (tx==None):
+ continue
+ if (tx.tex.image==None):
+ continue
+ #gen_mode=0
+ coord_mode=0
+
+ if (tx.texco&Blender.Texture.TexCo['REFL']):
+ gen_mode=1 # reflection
+ coord_mode=3
+ elif (tx.texco&Blender.Texture.TexCo['WIN']):
+ gen_mode=2 # reflection
+ coord_mode=3
+ layer=""
+
+ if (tx.mtCol and not have_primary):
+ layer="textures/diffuse"
+ have_primary=True
+ elif (tx.mtCol and have_primary and not have_detail):
+ layer="textures/detail"
+ detail_mix = tx.colfac
+ print("colfac: "+str(tx.colfac));
+ have_detail=True
+ elif (tx.mtNor):
+ layer="textures/normal"
+ elif (tx.mtSpec):
+ layer="textures/specular"
+
+ if (layer==""):
+ continue
+
+ img_file = tx.tex.image.getFilename()
+
+ #Agregado por Ariel, trajo muchos problemas, lo saco.
+ #img_file = Blender.sys.expandpath(tx.tex.image.getFilename())
+ #exp_dir = os.path.dirname(exporter.filename)
+ #img_file = os.path.relpath(os.path.abspath(img_file), exp_dir)
+
+ img_file = img_file.replace("\\", "/")
+
+ res.add(layer+"_tc",coord_mode)
+ tex_res = ObjectTree(None,"Texture")
+ tex_res._resource=True
+ tex_res._res_path=img_file
+
+ res.add(layer,tex_res)
+
+
+ if (have_detail):
+ res.add("params/detail_mix",detail_mix)
+ if (gen_mode!=0):
+ res.add("tex_gen",gen_mode)
+
+ res._res_path="local://"+str(len(exporter.resource_list))
+
+ exporter.resource_list.append(res)
+
+ res_ref = ObjectTree(None,"Material")
+ res_ref._resource=True
+ res_ref._res_path=res._res_path
+
+
+ exporter.material_caches[mat.getName()]=res_ref
+ return res
+
+def make_mesh_vertex_weights(node,skeleton):
+
+ mesh = node.getData()
+ verts=[]
+
+ groups=mesh.getVertGroupNames()
+ if (len(groups)==0):
+ return None
+
+ idx=0
+ for x in mesh.verts:
+ influences = mesh.getVertexInfluences(idx)
+ inflist=[]
+ for inf in influences:
+ name=inf[0]
+ if (not name in skeleton._bone_map):
+ continue # no bone for group, ignore
+ bone_idx=skeleton._bone_map[name]
+ inflist.append( float(bone_idx) )
+ inflist.append( inf[1] )
+
+ verts.append(inflist)
+ idx+=1
+
+
+ for i in xrange(len(verts)):
+
+ swaps=1
+ while( swaps > 0 ):
+
+ swaps=0
+ for j in xrange(len(verts[i])/2-1):
+ #small dirty bubblesort
+ if (verts[i][j*2+1] < verts[i][(j+1)*2+1]):
+
+ verts[i][j*2],verts[i][(j+1)*2]=verts[i][(j+1)*2],verts[i][j*2]
+
+ verts[i][j*2+1],verts[i][(j+1)*2+1]=verts[i][(j+1)*2+1],verts[i][j*2+1]
+
+ swaps+=1
+
+ if ((len(verts[i])/2)>MAX_WEIGHTS_PER_VERTEX):
+ #more than 4 weights, sort by most significant to least significant
+ new_arr=[]
+
+ for j in xrange(MAX_WEIGHTS_PER_VERTEX*2):
+ new_arr+=[verts[i][j]]
+
+ verts[i]=new_arr
+
+ #make all the weights add up to 1
+ max_w=0.0
+ count=len(verts[i])/2
+
+ for j in range(count):
+ #small dirty bubblesort
+ max_w+=verts[i][j*2+1]
+
+ if (max_w>0.0):
+ mult=1/max_w
+ for j in range(count):
+ verts[i][j*2+1]*=mult
+ #fill up empty slots
+ while ((len(verts[i])/2)<MAX_WEIGHTS_PER_VERTEX):
+ verts[i]+=[0,0] # add empty index
+
+ return verts
+
+
+
+def make_mesh(node,mesh,skeleton,exporter,applymatrix):
+
+
+ mesh_res=ObjectTree(None,"Mesh")
+ mesh_res._resource=True
+ mesh_res.add("resource/name",mesh.name)
+
+
+ #bake faces and surfaces
+
+ weights=None
+
+ if (skeleton!=None):
+ weights=make_mesh_vertex_weights(node,skeleton)
+
+ surfaces={}
+
+ for f in mesh.faces:
+ if (not f.mat in surfaces):
+ surfaces[f.mat]=Surface()
+ surfaces[f.mat]._vertex_weights=weights
+ surfaces[f.mat]._has_uv=mesh.hasFaceUV()
+ surfaces[f.mat]._has_color=mesh.hasVertexColours()
+ surfaces[f.mat]._mat=f.mat
+
+ surfaces[f.mat].insertFace(f)
+ #bake materials
+
+ for s in surfaces.values():
+ if (s._mat<0 or s._mat>=len(mesh.materials)):
+ continue
+ s._material=make_material(mesh.materials[s._mat],(mesh.mode&Blender.Mesh.Modes['TWOSIDED'])!=0,exporter)
+
+ #write surfaces
+ surf_idx=1
+ for x in surfaces.values():
+ x.convert(applymatrix)
+ x.write_to_res(mesh_res,surf_idx)
+ surf_idx+=1
+
+
+ mesh_res._res_path="local://"+str(len(exporter.resource_list))
+
+ exporter.resource_list.append(mesh_res)
+
+ res_ref = ObjectTree(None,"Mesh")
+ res_ref._resource=True
+ res_ref._res_path=mesh_res._res_path
+
+ return mesh_res
+
+def write_mesh(scene, node, tree,exporter):
+
+
+ mesh = node.getData()
+ tree._type="MeshInstance"
+
+ skeleton=tree
+
+ #find a skeleton
+
+ while( skeleton!=None and skeleton._type!="Skeleton" ):
+ skeleton=skeleton._parent
+
+ mat=get_local_matrix(node)
+
+ applymatrix=None
+
+ if (skeleton):
+ applymatrix=mat
+ else:
+ tree.add("transform/local",mat)
+
+ #is mesh cached
+ if (skeleton==None and mesh.name in exporter.mesh_caches):
+
+ global last_local
+
+ tree.add("mesh/mesh",exporter.mesh_caches[mesh.name])
+ return tree
+
+ #make mesh
+
+ mesh_res = make_mesh(node,mesh,skeleton,exporter,applymatrix)
+ tree.add("mesh/mesh",mesh_res)
+
+ if (skeleton==None):
+ exporter.mesh_caches[mesh.name]=mesh_res
+
+
+
+ return tree
+
+def write_armature_bone(bone,tree):
+
+ idx=len(tree._bone_map)
+ parent_idx=-1
+ if (bone.parent != None):
+ parent_idx = tree._bone_map[ bone.parent.name ]
+
+ prop="bones/"+str(idx)+"/"
+ mat = Matrix4x3()
+
+ mat.setBlenderMatrix( convert_matrix(bone.matrix['ARMATURESPACE']) )
+ if (bone.parent!=None):
+ mat_parent=Matrix4x3()
+ #mat_parent.scale(scale)
+ mat_parent.setBlenderMatrix( convert_matrix( bone.parent.matrix['ARMATURESPACE'] ))
+ mat_parent.invert()
+
+ mat.setBlenderMatrix( mat.getBlenderMatrix() * mat_parent.getBlenderMatrix() )
+
+ else:
+
+ pass; #mat.scale(scale)
+
+ tree.add(prop+"name",bone.name)
+ tree.add(prop+"parent",parent_idx)
+ tree.add(prop+"rest",mat)
+
+ tree._bone_map[ bone.name ] = idx # map bone to idx
+
+ for x in bone.children:
+
+ write_armature_bone(x,tree)
+
+
+
+def write_armature(scene, node, tree,exporter):
+
+ mat=get_local_matrix(node)
+ tree.add("transform/local",mat)
+
+ mesh = node.getData()
+ tree._type="Skeleton"
+ tree._bone_map={}
+ bone_map={}
+
+ for x in node.data.bones.values():
+
+ if (x.parent != None):
+ continue
+
+ write_armature_bone(x,tree)
+ return tree
+
+
+def write_camera(scene, node, tree,exporter):
+
+
+ mesh = node.getData()
+ tree._type="Camera"
+
+ mat=get_local_matrix(node)
+ tree.add("transform/local",mat)
+
+ return tree
+
+def write_empty(scene, node, tree,exporter):
+
+ mat=get_local_matrix(node)
+ tree.add("transform/local",mat)
+ tree._type="Spatial"
+
+ return tree
+
+
+
+writers = {"Mesh": write_mesh, "Armature":write_armature, "Empty":write_empty, "Camera":write_camera }
+
+
+def get_local_matrix(node):
+
+ mat_bm=node.getMatrix('worldspace').copy()
+
+ if (node.getParent()!=None):
+ mat_parent_bm=node.getParent().getMatrix('worldspace').copy()
+ mat_parent_bm.invert()
+
+ mat_bm = mat_bm * mat_parent_bm
+
+
+ if (node.getType()=="Camera"):
+ mat2=Matrix4x3()
+ mat2.set_rotation(Vector3([1,0,0]),-math.pi/2.0)
+ mat2bm = mat2.getBlenderMatrix()
+ mat_bm = mat2bm * mat_bm
+
+ mat=Matrix4x3()
+ mat.setBlenderMatrix(convert_matrix(mat_bm))
+
+ return mat
+
+
+def get_unscaled_matrix(node):
+
+ mat_bm=convert_matrix(node.getMatrix('worldspace'))
+ mat=Matrix4x3()
+ mat.setBlenderMatrix(mat_bm)
+ scale=mat.getScale()
+# print("--"+node.getName()+" "+str(scale.x)+","+str(scale.y)+","+str(scale.z))
+# print(mat.m)
+ mat.clearScale()
+# print(mat.getBlenderMatrix().determinant());
+
+ if (node.getParent()!=None):
+ mat_parent_bm=convert_matrix(node.getParent().getMatrix('worldspace'))
+
+ mat_parent=Matrix4x3()
+ mat_parent.setBlenderMatrix(mat_parent_bm)
+ mat_parent.clearScale()
+ mat_parent.invert()
+ mat_scale=mat.getScale()
+ mat_parent_scale=mat.getScale()
+
+ if (False and node.getName()=="Cylinder.002"):
+
+ print("Morth1? "+str(mat.m[0][0]*mat.m[1][0]+mat.m[0][1]*mat.m[1][1]+mat.m[0][2]*mat.m[1][2]))
+ print("Morth2? "+str(mat.m[0][0]*mat.m[2][0]+mat.m[0][1]*mat.m[2][1]+mat.m[0][2]*mat.m[2][2]))
+ print("Morth3? "+str(mat.m[1][0]*mat.m[2][0]+mat.m[1][1]*mat.m[2][1]+mat.m[1][2]*mat.m[2][2]))
+ print("North1? "+str(mat_parent.m[0][0]*mat_parent.m[1][0]+mat_parent.m[0][1]*mat_parent.m[1][1]+mat_parent.m[0][2]*mat_parent.m[1][2]))
+ print("North2? "+str(mat_parent.m[0][0]*mat_parent.m[2][0]+mat_parent.m[0][1]*mat_parent.m[2][1]+mat_parent.m[0][2]*mat_parent.m[2][2]))
+ print("North3? "+str(mat_parent.m[1][0]*mat_parent.m[2][0]+mat_parent.m[1][1]*mat_parent.m[2][1]+mat_parent.m[1][2]*mat_parent.m[2][2]))
+ print(mat_parent.getBlenderMatrix().determinant());
+
+ #print(m
+ #print(m
+
+ mat_bm = mat.getBlenderMatrix();
+ mat_parent_bm = mat_parent.getBlenderMatrix();
+ mat_bm = mat_bm * mat_parent_bm
+ mat.setBlenderMatrix(mat_bm)
+ """
+ mat_parent.mult_by(mat)
+ mat=mat_parent
+ """
+ """
+ print("scale_mat "+str(mat_scale.x)+","+str(mat_scale.y)+","+str(mat_scale.z))
+ print("scale_mat_parent "+str(mat_parent_scale.x)+","+str(mat_parent_scale.y)+","+str(mat_parent_scale.z))
+
+ print("orth1? "+str(mat.m[0][0]*mat.m[1][0]+mat.m[0][1]*mat.m[1][1]+mat.m[0][2]*mat.m[1][2]))
+ print("orth2? "+str(mat.m[0][0]*mat.m[2][0]+mat.m[0][1]*mat.m[2][1]+mat.m[0][2]*mat.m[2][2]))
+ print("orth3? "+str(mat.m[1][0]*mat.m[2][0]+mat.m[1][1]*mat.m[2][1]+mat.m[1][2]*mat.m[2][2]))
+ """
+ #print(m
+ #print(mat.m)
+ wscale=mat.getScale()
+
+ return mat,scale
+
+def write_object(scene,node,tree,exporter):
+
+ tree_node=ObjectTree(tree,"",node.getName())
+
+ if writers.has_key(node.getType()):
+ tree_node=writers[node.getType()](scene,node, tree_node,exporter)
+ else:
+ tree_node=None#write_dummy(node,tree)
+
+ if (tree_node != None):
+
+ for node in get_children_objects(scene,node):
+ write_object(scene, node, tree_node,exporter)
+
+ tree._children+=[tree_node]
+
+
+def export_scene(filename):
+
+ exporter = ExporterData(filename)
+ scene = None
+ object = None
+
+ scene = Blender.Scene.GetCurrent()
+ if not scene:
+ return
+ tree = ObjectTree(None,"Spatial","Scene")
+ write_scene(scene, tree,exporter)
+
+ if widget_values["export_lua"]:
+ write_godot_lua(tree, filename)
+ else:
+ write_godot_xml(tree,filename,exporter)
+
+def write_scene(scene, tree,exporter):
+
+ tree._name=scene.getName()
+ for node in get_root_objects(scene):
+ write_object(scene,node, tree,exporter )
+
+""" --------- """
+""" ANIMATION """
+""" --------- """
+
+class Animation:
+ class Track:
+
+ def insertKey(self,time,mat):
+
+ ofs = mat.getPos()
+ rot = Quat(mat)
+ scale = mat.getScale();
+
+ self.xform_keys.append( time )
+ self.xform_keys.append( 1.0 ) # transition
+ self.xform_keys.append( ofs.x )
+ self.xform_keys.append( ofs.y )
+ self.xform_keys.append( ofs.z )
+
+ self.xform_keys.append( -rot.x )
+ self.xform_keys.append( -rot.y )
+ self.xform_keys.append( -rot.z )
+ self.xform_keys.append( rot.w )
+
+ self.xform_keys.append( scale.x )
+ self.xform_keys.append( scale.y )
+ self.xform_keys.append( scale.z )
+
+
+ def _optimized(self,arr):
+ _new=[]
+ #remove irrelevant keys
+ for i in range( len(arr) ):
+ if (i>0 and i<(len(arr)-1) and eq_vec(arr[i]["value"],arr[i+1]["value"]) and eq_vec(arr[i]["value"],arr[i-1]["value"])):
+ continue
+ _new.append(arr[i])
+
+ return _new
+ def optimize(self):
+ #self.loc_keys=self._optimized(self.loc_keys)
+ #self.rot_keys=self._optimized(self.rot_keys)
+ #self.scale_keys=self._optimized(self.scale_keys)
+ pass
+
+ def _get_track_array3(self,keys):
+ _arr=[]
+ for x in keys:
+ _arr.append(x["time"])
+ v=x["value"]
+ _arr.append(v.x)
+ _arr.append(v.y)
+ _arr.append(v.z)
+ return _arr;
+
+ def _get_track_array4(self,keys):
+ _arr=[]
+ for x in keys:
+ _arr.append(x["time"])
+ v=x["value"]
+ _arr.append(-v.x)
+ _arr.append(-v.y)
+ _arr.append(-v.z)
+ _arr.append(v.w)
+ return _arr;
+
+
+
+ def write_to_res(self,res,i):
+ prep="tracks/"+str(i)+"/"
+ res.add(prep+"type","transform")
+ res.add(prep+"path",self.path)
+
+ res.add(prep+"keys",self.xform_keys)
+
+
+ def __init__(self):
+ self.xform_keys=[]
+ self.path=""
+
+ def make_res(self):
+
+ res = ObjectTree(None,"Animation")
+ res._resource=True
+ res.add("length",self.length);
+ res.add("loop",self.loop);
+ idx=0
+ for t in self.tracks.values():
+ t.optimize()
+ t.write_to_res(res,idx)
+ idx=idx+1
+ return res
+
+ def __init__(self):
+ self.tracks={}
+ self.fps=30
+ self.length=0
+
+
+def write_animation_bone(scene,node,anim,path,bone,frame):
+
+ rest = convert_matrix(bone.matrix['ARMATURESPACE'])
+
+
+ if (bone.parent!=None):
+
+ rest_parent = convert_matrix( bone.parent.matrix['ARMATURESPACE'] )
+ rest_parent.invert()
+ rest = rest * rest_parent
+
+ bone_path = path+":"+bone.name;
+
+ if (bone_path not in anim.tracks):
+ t = Animation.Track()
+ t.path=bone_path
+ anim.tracks[bone_path] = t
+ else:
+ t=anim.tracks[bone_path]
+
+ pose_bone = node.getPose().bones[bone.name]
+
+
+ pose = convert_matrix(pose_bone.poseMatrix)
+
+ if (bone.parent!=None):
+
+ mat_parent=convert_matrix( pose_bone.parent.poseMatrix )
+ mat_parent.invert()
+
+ pose = pose * mat_parent
+
+ # pose should actually be the transform from pose to rest
+
+ rest.invert()
+ pose = pose * rest
+
+ mat43 = Matrix4x3()
+ mat43.setBlenderMatrix(pose)
+
+ t.insertKey(frame/float(anim.fps),mat43)
+
+
+
+def write_animation_armature(scene,node,anim,path,frame):
+
+ for x in node.data.bones.values():
+
+ write_animation_bone(scene,node,anim,path,x,frame)
+
+
+def write_animation_object(scene,node,anim,path,frame,parent_type):
+
+ if not writers.has_key(node.getType()):
+ return
+
+
+ new_path=path+node.getName()
+
+ if (path!=""):
+ path=path+"/"+node.getName()
+ else:
+ path=node.getName()
+
+ if (node.getType()=="Armature" or node.getIpo()!=None):
+ #only export if it has animation
+ if (path not in anim.tracks):
+ t = Animation.Track()
+ t.path=path
+ anim.tracks[path] = t
+ else:
+ t=anim.tracks[path]
+
+
+ if (parent_type!="Armature"):
+ t.insertKey(frame/float(anim.fps),get_local_matrix(node))
+
+ if (node.getType()=="Armature"):
+ write_animation_armature(scene,node,anim,path,frame)
+ return # children of armature will not be animated
+
+
+ for node in get_children_objects(scene,node):
+ write_animation_object(scene, node, anim, path, frame, node.getType())
+
+
+def write_animation(scene, anim, frame):
+
+ for node in get_root_objects(scene):
+ write_animation_object(scene,node, anim,"",frame,"")
+
+def export_animation(filename, end_frame = -1, loop = None):
+
+ anim = Animation()
+ anim.fps=Blender.Scene.GetCurrent().getRenderingContext().fps
+
+ if loop == None:
+ anim.loop=widget_values["anim_loop"]
+ else:
+ anim.loop = loop
+
+ print("end_frame param: %d"%end_frame)
+ if end_frame == -1:
+ end_frame = Blender.Get("endframe")
+
+ anim.length=(end_frame-Blender.Get("staframe")+1)/float(anim.fps)
+ print("frames "+str((end_frame-Blender.Get("staframe")+1)))
+ print("start: %d, end %d, fps %d, length %f" % (Blender.Get("staframe"), end_frame, anim.fps, anim.length));
+
+ scene = Blender.Scene.GetCurrent()
+ if not scene:
+ return
+
+ for frame in range( Blender.Get('staframe'), end_frame+1):
+ Blender.Set("curframe",frame)
+ write_animation(scene,anim,frame)
+
+ anim_res = anim.make_res()
+ res_name = filename
+
+ if(res_name.rfind(".")!=-1):
+ res_name=res_name[:res_name.rfind(".")]
+ if(res_name.rfind("/")!=-1):
+ res_name=res_name[res_name.rfind("/")+1:]
+ if(res_name.rfind("\\")!=-1):
+ res_name=res_name[res_name.rfind("\\")+1:]
+
+ anim_res.add("resource/name",res_name)
+
+ if widget_values['export_lua']:
+ write_godot_lua(anim_res,filename)
+ else:
+ write_godot_xml(anim_res,filename,None)
+
+""" -------------- """
+""" SERIALIZATION """
+""" ------------- """
+
+def tw(f,t,st):
+ for x in range(t):
+ f.write("\t")
+ nl = True
+ if len(st) > 0 and st[-1] == "#":
+ nl = False
+ st = st[:-1]
+ f.write(st)
+ if nl:
+ f.write("\n")
+
+def write_property_godot(f,tab,name,value):
+
+# print(str(value))
+# print(type(value))
+ if (type(value)==str):
+
+ tw(f,tab,'<string name="'+name+'">')
+ value=value.replace('"','\\&quot;')
+ tw(f,tab+1,'"'+value+'"');
+ tw(f,tab,'</string>')
+ elif (type(value)==bool):
+ tw(f,tab,'<bool name="'+name+'">')
+ if (value):
+ tw(f,tab+1,'True');
+ else:
+ tw(f,tab+1,'False');
+ tw(f,tab,'</bool>')
+ elif (type(value)==int):
+ tw(f,tab,'<int name="'+name+'">')
+ tw(f,tab+1,str(value));
+ tw(f,tab,'</int>')
+ elif (type(value)==float):
+ tw(f,tab,'<real name="'+name+'">')
+ tw(f,tab+1,str(value));
+ tw(f,tab,'</real>')
+ elif (type(value)==dict):
+ tw(f,tab,'<dictionary name="'+name+'">')
+ for x in value:
+ write_property_godot(f,tab+1,"key",x)
+ write_property_godot(f,tab+1,"value",value[x])
+ tw(f,tab,'</dictionary>')
+ elif (isinstance(value,ObjectTree)):
+ if (not value._resource):
+ print("ERROR: Not a resource!!")
+ return
+ if (value._res_path!=""):
+
+ tw(f,tab,'<resource name="'+name+'" resource_type="'+value._type+'" path="'+value._res_path+'">')
+ tw(f,tab,'</resource>')
+ else:
+ tw(f,tab,'<resource name="'+name+'" resource_type="'+value._type+'">')
+ tw(f,tab+1,'<object type="'+value._type+'">')
+ tw(f,tab+2,'<resource>')
+
+ for x in value._properties:
+ write_property_godot(f,tab+3,x[0],x[1])
+
+ tw(f,tab+2,'</resource>')
+ tw(f,tab+1,'</object>')
+ tw(f,tab,'</resource>')
+ elif (isinstance(value,Color)):
+ tw(f,tab,'<color name="'+name+'">')
+ tw(f,tab+1,str(value.r)+", "+str(value.g)+", "+str(value.b)+", "+str(value.a));
+ tw(f,tab,'</color>')
+ elif (isinstance(value,Vector3)):
+ tw(f,tab,'<vector3 name="'+name+'">')
+ tw(f,tab+1,str(value.x)+", "+str(value.y)+", "+str(value.z));
+ tw(f,tab,'</vector3>')
+ elif (isinstance(value,Quat)):
+ tw(f,tab,'<quaternion name="'+name+'">')
+ tw(f,tab+1,str(-value.x)+", "+str(-value.y)+", "+str(-value.z)+", "+str(value.w));
+ tw(f,tab,'</quaternion>')
+ elif (isinstance(value,Matrix4x3)): # wtf, blender matrix?
+ tw(f,tab,'<transform name="'+name+'" >')
+ s=""
+ for i in range(3):
+ for j in range(3):
+ s+=", "+str(value.m[j][i])
+
+ for i in range(3):
+ s+=", "+str(value.m[i][3])
+ s=s[1:]
+ tw(f,tab+1,s);
+ tw(f,tab,'</transform>')
+
+ elif (type(value)==list):
+ if (len(value)==0):
+ return
+ first=value[0]
+ if (type(first)==int):
+
+ tw(f,tab,'<int_array name="'+name+'" len="'+str(len(value))+'">')
+ arr=""
+ for i in range(len(value)):
+ if (i>0):
+ arr+=", "
+ arr+=str(value[i])
+ tw(f,tab+1,arr)
+ tw(f,tab,'</int_array>')
+ elif (type(first)==float):
+
+ tw(f,tab,'<real_array name="'+name+'" len="'+str(len(value))+'">')
+ arr=""
+ for i in range(len(value)):
+ if (i>0):
+ arr+=", "
+ arr+=str(value[i])
+ tw(f,tab+1,arr)
+ tw(f,tab,'</real_array>')
+ elif (type(first)==str):
+
+ tw(f,tab,'<string_array name="'+name+'" len="'+str(len(value))+'">')
+ arr=""
+ for i in range(len(value)):
+ if (i>0):
+ arr+=", "
+ arr+=str('"'+value[i]+'"')
+ tw(f,tab+1,arr)
+ tw(f,tab,'</string_array>')
+ elif (isinstance(first,Vector3)):
+
+ tw(f,tab,'<vector3_array name="'+name+'" len="'+str(len(value))+'">')
+ arr=""
+ for i in range(len(value)):
+ if (i>0):
+ arr+=", "
+ arr+=str(str(value[i].x)+','+str(value[i].y)+','+str(value[i].z))
+ tw(f,tab+1,arr)
+ tw(f,tab,'</vector3_array>')
+ elif (isinstance(first,Color)):
+
+ tw(f,tab,'<color_array name="'+name+'" len="'+str(len(value))+'">')
+ arr=""
+ for i in range(len(value)):
+ if (i>0):
+ arr+=", "
+ arr+=str(str(value[i].r)+','+str(value[i].g)+','+str(value[i].b)+','+str(value[i].a))
+ tw(f,tab+1,arr)
+ tw(f,tab,'</color_array>')
+ elif (type(first)==dict):
+
+ tw(f,tab,'<array name="'+name+'" len="'+str(len(value))+'">')
+ for i in range(len(value)):
+ write_property_godot(f,tab+1,str(i+1),value[i])
+ tw(f,tab,'</array>')
+
+
+
+def write_node_godot(f,tab,tree,path,root=False):
+
+ if (root or not tree._resource):
+ tw(f,tab,'<object type="'+tree._type+'">')
+ tw(f,tab+1,'<dictionary name="__xml_meta__" type="dictionary">')
+ write_property_godot(f,tab+3,"key","name")
+ write_property_godot(f,tab+3,"value",tree._name)
+ if (path!=""):
+ write_property_godot(f,tab+3,"key","path")
+ write_property_godot(f,tab+3,"value",path)
+
+ tw(f,tab+1,'</dictionary>')
+ else:
+ if (tree._res_path!=""):
+ tw(f,tab,'<resource type="'+tree._type+'" path="'+tree._res_path+'">')
+ else:
+ tw(f,tab,'<resource type="'+tree._type+'">')
+
+
+ for x in tree._properties:
+ write_property_godot(f,tab+1,x[0],x[1])
+
+ if (root or not tree._resource):
+ tw(f,tab,'</object>')
+ else:
+ tw(f,tab,'</resource>')
+
+ if (path==""):
+ path="."
+ else:
+ if (path=="."):
+ path=tree._name
+ else:
+ path=path+"/"+tree._name
+ #path="."
+
+ for x in tree._children:
+ write_node_godot(f,tab,x,path)
+
+def write_godot_xml(tree,fname,exporter):
+
+ f=open(fname,"wb")
+ f.write('<?xml version="1.0" encoding="UTF-8" ?>\n')
+ if (not tree._resource):
+ f.write('<object_file magic="SCENE" version="0.99">\n')
+ else:
+ f.write('<object_file magic="RESOURCE" version="0.99">\n')
+
+ tab=1
+
+ if (exporter!=None):
+ for x in exporter.resource_list:
+ write_node_godot(f,tab,x,"")
+
+ write_node_godot(f,tab,tree,"",True)
+ f.write('</object_file>\n')
+
+
+def write_property_lua(f, tab, name, value, pref = ""):
+
+ tw(f, tab, '%s{ name = "%s",' % (pref, name))
+ tab = tab + 1
+
+ if (type(value)==str):
+
+ tw(f, tab, 'value = "%s",' % value)
+ tw(f, tab, 'type = "string",')
+
+ elif (type(value)==bool):
+
+
+ if (value):
+ tw(f, tab, 'value = true,')
+ else:
+ tw(f, tab, 'value = false,')
+
+ tw(f, tab, 'type = "bool",')
+
+ elif (type(value)==int):
+
+ tw(f, tab, 'type = "int",')
+ tw(f, tab, 'value = %d,' % value)
+
+ elif (type(value)==float):
+
+ tw(f, tab, 'type = "real",')
+ tw(f, tab, 'value = %f,' % value)
+
+ elif (type(value)==dict):
+
+ tw(f, tab, 'type = "dictionary",')
+ for x in value:
+ write_property_lua(f,tab,x,value[x])
+
+ elif (isinstance(value,ObjectTree)):
+ if (not value._resource):
+ print("ERROR: Not a resource!!")
+ tw(f, tab-1, "},")
+ return
+
+ tw(f, tab, 'type = "resource",')
+ tw(f, tab, 'resource_type = "%s",' % value._type)
+
+ if (value._res_path!=""):
+
+ tw(f, tab, 'path = "%s",' % value._res_path)
+
+ else:
+
+ tw(f, tab, "value = {")
+ tab = tab + 1
+ tw(f, tab, 'type = "%s",' % value._type)
+
+ for x in value._properties:
+ write_property_lua(f,tab,x[0],x[1])
+
+ tab = tab - 1
+ tw(f, tab, "},")
+
+ elif (isinstance(value,Color)):
+
+ tw(f, tab, 'type = "color",')
+ tw(f, tab, 'value = { %.20f, %.20f, %.20f, %.20f },' % (value.r, value.g, value.b, value.a))
+
+ elif (isinstance(value,Vector3)):
+
+ tw(f, tab, 'type = "vector3",')
+ tw(f, tab, 'value = { %.20f, %.20f, %.20f },' % (value.x, value.y, value.z))
+
+ elif (isinstance(value,Quat)):
+
+ tw(f, tab, 'type = "quaternion",')
+ tw(f, tab, 'value = { %.20f, %.20f, %.20f, %.20f },' % (-value.x, -value.y, -value.z, value.w))
+
+ elif (isinstance(value,Matrix4x3)): # wtf, blender matrix?
+
+ tw(f, tab, 'type = "transform",')
+ tw(f, tab, 'value = { #')
+ for i in range(3):
+ for j in range(3):
+ f.write("%.20f, " % value.m[j][i])
+
+ for i in range(3):
+ f.write("%.20f, " % value.m[i][3])
+
+ f.write("},\n")
+
+ elif (type(value)==list):
+ if (len(value)==0):
+ tw(f, tab-1, "},")
+ return
+ first=value[0]
+ if (type(first)==int):
+
+ tw(f, tab, 'type = "int_array",')
+ tw(f, tab, 'value = { #')
+ for i in range(len(value)):
+ f.write("%d, " % value[i])
+ f.write(" },\n")
+
+ elif (type(first)==float):
+
+ tw(f, tab, 'type = "real_array",')
+ tw(f, tab, 'value = { #')
+ for i in range(len(value)):
+ f.write("%.20f, " % value[i])
+ f.write(" },\n")
+
+
+ elif (type(first)==str):
+
+ tw(f, tab, 'type = "string_array",')
+ tw(f, tab, 'value = { #')
+ for i in range(len(value)):
+ f.write('"%s", ' % value[i])
+ f.write(" },\n")
+
+ elif (isinstance(first,Vector3)):
+
+ tw(f, tab, 'type = "vector3_array",')
+ tw(f, tab, 'value = { #')
+ for i in range(len(value)):
+ f.write("{ %.20f, %.20f, %.20f }, " % (value[i].x, value[i].y, value[i].z))
+ f.write(" },\n")
+
+ elif (isinstance(first,Color)):
+
+ tw(f, tab, 'type = "color_array",')
+ tw(f, tab, 'value = { #')
+ for i in range(len(value)):
+ f.write("{ %.20f, %.20f, %.20f, %.20f }, " % (value[i].r, value[i].g, value[i].b, value[i].a))
+ f.write(" },\n")
+
+ elif (type(first)==dict):
+
+ tw(f, tab, 'type = "dict_array",')
+ tw(f, tab, 'value = {')
+
+ for i in range(len(value)):
+ write_property_lua(f,tab+1,str(i+1),value[i])
+
+ tw(f, tab, '},')
+
+
+ tw(f, tab-1, "},")
+
+
+""" -------------- """
+""" SERIALIZATION LUA """
+""" ------------- """
+
+def write_node_lua(f,tab,tree,path):
+
+ tw(f, tab, '{ type = "%s",' % tree._type)
+
+ if not tree._resource:
+ tw(f, tab+1, 'meta = {')
+ write_property_lua(f, tab+3, "name", tree._name)
+ if path != "":
+ write_property_lua(f, tab+3, "path", path)
+ tw(f, tab+1, '},')
+
+ tw(f, tab+1, "properties = {")
+ for x in tree._properties:
+ write_property_lua(f,tab+2,x[0],x[1])
+ tw(f, tab+1, "},")
+
+ tw(f, tab, '},')
+
+
+ if (path==""):
+ path="."
+ else:
+ if (path=="."):
+ path=tree._name
+ else:
+ path=path+"/"+tree._name
+ #path="."
+ for x in tree._children:
+ write_node_lua(f,tab,x,path)
+
+def write_godot_lua(tree,fname):
+ f=open(fname,"wb")
+ f.write("return {\n")
+
+ f.write('\tmagic = "SCENE",\n')
+ tab = 1
+
+ write_node_lua(f,tab,tree,"")
+
+ f.write("}\n\n")
+
+
+widget_values={}
+
+def action_path_change_callback(event, val):
+
+ def callback(fname):
+ widget_values["actions_scheme"] = fname
+ Blender.Window.FileSelector(callback, "Save Action Scheme Name", widget_values["actions_scheme"])
+
+def scene_path_change_callback(event,val):
+
+ def callback(fname):
+ widget_values["scene_path"]=fname
+
+ Blender.Window.FileSelector(callback, "Save Scene XML",widget_values["scene_path"])
+
+def scene_export_callback(event,val):
+ export_scene( widget_values["scene_path"] )
+
+def scene_lamps_cameras_changed(event,val):
+
+ widget_values["scene_lamps_cameras"]=val
+
+def anim_path_change_callback(event,val):
+
+ def callback(fname):
+ widget_values["anim_path"]=fname
+
+ Blender.Window.FileSelector(callback, "Save Anim XML",widget_values["anim_path"])
+
+def is_number(n):
+
+ try:
+ int(n)
+ except:
+ return False
+ return True
+
+def action_export_callback(event, val):
+
+ import string
+
+ idx = widget_values["actions_scheme"].rfind(".")
+ if idx == -1:
+ pref = widget_values["actions_scheme"]
+ ext = ".xml"
+ else:
+ pref = widget_values["actions_scheme"][:idx]
+ ext = widget_values["actions_scheme"][idx:]
+
+ print("scheme is ", pref, ext)
+
+ actions = Blender.Armature.NLA.GetActions()
+ for k in actions.keys():
+
+ l = string.split(k, "$");
+ if len(l) <= 1:
+ continue
+
+ loop = 1
+ endf = 0
+ for v in l:
+ if v == "nl":
+ loop = 0
+ if is_number(v):
+ endf = int(v)
+
+ if endf == 0:
+ continue
+
+ fname = pref + l[0] + ext
+ print("fname is "+fname)
+
+ objects = Blender.Object.Get()
+ for o in objects:
+ if o.getType() == "Armature":
+ actions[k].setActive(o)
+
+ print("writing with duration "+str(endf))
+ export_animation(fname, endf, loop)
+
+def anim_export_callback(event,val):
+ export_animation( widget_values["anim_path"] )
+
+def anim_fps_changed(event,val):
+
+ widget_values["anim_fps"]=val
+
+def anim_selected_changed(event,val):
+
+ widget_values["anim_selected"]=val
+
+def anim_loop_changed(event,val):
+
+ widget_values["anim_loop"]=val
+
+def export_lua_changed(event, val):
+ widget_values["export_lua"] = val
+
+def close_script(event,val):
+#force a bug, because otherwise blender won't unload the script
+ Blender.Draw.Exit()
+
+def draw():
+ Blender.Draw.Label("Godot Export v."+VERSION+"."+godot_revision+" (c) 2008 Juan Linietsky, Ariel Manzur.", 10,260,400,10);
+ Blender.Draw.Label("Export Scene", 20,200,150,10);
+ Blender.Draw.String(widget_values["scene_path"], 10,40, 170, 300, 20, "",398)
+ Blender.Draw.Button("Choose", 0,340, 170, 70, 20, "",scene_path_change_callback)
+ Blender.Draw.Button("Export", 0,410, 170, 70, 20, "",scene_export_callback)
+ Blender.Draw.Toggle("Lamps & Cameras", 0,40, 140, 140, 20, widget_values["scene_lamps_cameras"],"",scene_lamps_cameras_changed)
+
+ Blender.Draw.Label("Export Animation", 20,120,150,10);
+ Blender.Draw.String(widget_values["anim_path"], 11, 40, 90, 300, 20, "",398)
+ Blender.Draw.Button("Choose", 0,340, 90, 70, 20, "",anim_path_change_callback)
+ Blender.Draw.Button("Export", 0,410, 90, 70, 20, "",anim_export_callback)
+ Blender.Draw.Slider("FPS: ", 0, 40, 60, 120, 20,widget_values["anim_fps"],1,60,0,"",anim_fps_changed)
+ Blender.Draw.Toggle("Only Selected", 0,180, 60, 120, 20, widget_values["anim_selected"],"",anim_selected_changed)
+ Blender.Draw.Toggle("Loop", 0,320, 60, 60, 20, widget_values["anim_loop"],"",anim_loop_changed)
+ Blender.Draw.Toggle("Export Lua", 0, 400, 60, 60, 20, widget_values["export_lua"], "", export_lua_changed)
+
+ Blender.Draw.Label("Export Actions", 20,45,150,10);
+ Blender.Draw.Label("Prefix", 40, 20, 50, 10)
+ Blender.Draw.String(widget_values["actions_scheme"], 0, 40, 20, 300, 20, "",398)
+ Blender.Draw.Button("Choose", 0,340, 20, 70, 20, "",action_path_change_callback)
+ Blender.Draw.Button("Export", 0,410, 20, 70, 20, "",action_export_callback)
+# # Blender.Draw.Button("Close", 0,410, 20, 70, 20, "",close_script)
+
+widget_values["scene_path"]="scene.xml"
+widget_values["anim_path"]="animation.xres"
+widget_values["anim_fps"]=25
+widget_values["anim_selected"]=0
+widget_values["anim_loop"]=1
+widget_values["scene_lamps_cameras"]=0
+widget_values["export_lua"]=0
+widget_values["actions_scheme"] = "action_.xml"
+
+def event(ev, val):
+ return None
+
+def button_event(ev):
+ return None
+
+Blender.Draw.Register(draw, event, button_event)