diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /tools/export | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'tools/export')
-rw-r--r-- | tools/export/blender25/io_scene_dae/__init__.py | 180 | ||||
-rw-r--r-- | tools/export/blender25/io_scene_dae/export_dae.py | 1217 | ||||
-rw-r--r-- | tools/export/export_lua.py | 209 | ||||
-rw-r--r-- | tools/export/godot_export.py | 1842 |
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('"','\\"') + 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) |