diff options
Diffstat (limited to 'tools/export')
| -rw-r--r-- | tools/export/blender25/install.txt | 16 | ||||
| -rw-r--r-- | tools/export/blender25/io_scene_dae/__init__.py | 32 | ||||
| -rw-r--r-- | tools/export/blender25/io_scene_dae/export_dae.py | 602 |
3 files changed, 477 insertions, 173 deletions
diff --git a/tools/export/blender25/install.txt b/tools/export/blender25/install.txt index b245bbf3df..049af8848e 100644 --- a/tools/export/blender25/install.txt +++ b/tools/export/blender25/install.txt @@ -5,19 +5,7 @@ Godot Author's Own Collada Exporter scripts/addons folder (You will see many other io_scene_blahblah like folders). Copy the entire dir, not just the contents, make it just like the others. -2) Go to Blender settings and enable the "Khronos Collada" plugin +2) Go to Blender settings and enable the "Better Collada" plugin 3) Enjoy proper Collada export. -4) If it's broken, you can: - a) Flame the Blender developers in their mailing list for changing - the API every month. - b) Flame the Blender developers for not giving a home to this script - and mantaining it themselves, instead choosing to use the huge, - bloated and buggy OpenCollada based implementation that is as big - as Blender itself, while this script is a little over 1000 lines - of code, works better and has more features. - c) Cry to the poor Godot developers who are busy enough to fix it, - because they are good people and will fix it for you anyway (submit - an issue to github). - d) Be a Hero, save the day, fix it yourself and submit a pull request to - github with the changes. +4) If it's broken, contact us.
\ No newline at end of file diff --git a/tools/export/blender25/io_scene_dae/__init__.py b/tools/export/blender25/io_scene_dae/__init__.py index fe8a383383..b3e3f70cf0 100644 --- a/tools/export/blender25/io_scene_dae/__init__.py +++ b/tools/export/blender25/io_scene_dae/__init__.py @@ -19,14 +19,14 @@ # <pep8-80 compliant> bl_info = { - "name": "Khronos Collada format", + "name": "Better Collada Exporter", "author": "Juan Linietsky", "blender": (2, 5, 8), "api": 38691, "location": "File > Import-Export", - "description": ("Export DAE Scenes"), + "description": ("Export DAE Scenes, This plugin actually works better! otherwise contact me."), "warning": "", - "wiki_url": ("None"), + "wiki_url": ("http://www.godotengine.org"), "tracker_url": "", "support": 'OFFICIAL', "category": "Import-Export"} @@ -83,6 +83,17 @@ class ExportDAE(bpy.types.Operator, ExportHelper): description="Apply modifiers to mesh objects (on a copy!).", default=True, ) + use_tangent_arrays = BoolProperty( + name="Tangent Arrays", + description="Export Tangent and Binormal arrays (for normalmapping).", + default=False, + ) + use_triangles = BoolProperty( + name="Triangulate", + description="Export Triangles instead of Polygons.", + default=False, + ) + use_copy_images = BoolProperty( name="Copy Images", description="Copy Images (create images/ subfolder)", @@ -108,11 +119,17 @@ class ExportDAE(bpy.types.Operator, ExportHelper): description=("Export all actions for the first armature found in separate DAE files"), default=False, ) + use_anim_skip_noexp = BoolProperty( + name="Skip (-noexp) Actions", + description="Skip exporting of actions whose name end in (-noexp). Useful to skip control animations.", + default=True, + ) 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 " @@ -121,16 +138,13 @@ class ExportDAE(bpy.types.Operator, ExportHelper): soft_min=1, soft_max=16, default=6.0, ) + use_metadata = BoolProperty( name="Use Metadata", default=True, options={'HIDDEN'}, ) - export_shapekeys = BoolProperty( - name="Export Shape Keys", - default=False, - ) - + @property def check_extension(self): return True#return self.batch_mode == 'OFF' @@ -166,7 +180,7 @@ class ExportDAE(bpy.types.Operator, ExportHelper): def menu_func(self, context): - self.layout.operator(ExportDAE.bl_idname, text="Khronos Collada (.dae)") + self.layout.operator(ExportDAE.bl_idname, text="Better Collada (.dae)") def register(): diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py index 38c5c3b723..a92b97b8a9 100644 --- a/tools/export/blender25/io_scene_dae/export_dae.py +++ b/tools/export/blender25/io_scene_dae/export_dae.py @@ -43,6 +43,7 @@ import time import math # math.pi import shutil import bpy +import bmesh from mathutils import Vector, Matrix #according to collada spec, order matters @@ -51,12 +52,14 @@ 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 +S_MORPH=5 +S_SKIN=6 +S_CONT=7 +S_CAMS=8 +S_LAMPS=9 +S_ANIM_CLIPS=10 +S_NODES=11 +S_ANIM=12 CMP_EPSILON=0.0001 @@ -123,12 +126,25 @@ class DaeExporter: 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) + if (self.color!=None): + tup = tup + (self.color.x,self.color.y,self.color.z) + if (self.tangent!=None): + tup = tup + (self.tangent.x,self.tangent.y,self.tangent.z) + if (self.bitangent!=None): + tup = tup + (self.bitangent.x,self.bitangent.y,self.bitangent.z) + #for t in self.bones: + # tup = tup + (t) + #for t in self.weights: + # tup = tup + (t) + 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.tangent = None + self.bitangent = None + self.color = None self.uv = [] self.uv2 = Vector( (0.0,0.0) ) self.bones=[] @@ -281,9 +297,9 @@ class DaeExporter: 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,'<index_of_refraction>'+str(material.specular_ior)+'</index_of_refraction>') self.writel(S_FX,4,'<extra>') self.writel(S_FX,5,'<technique profile="FCOLLADA">') @@ -315,15 +331,138 @@ class DaeExporter: return matid - def export_mesh(self,node,armature=None,shapename=None): + def export_mesh(self,node,armature=None,skeyindex=-1,skel_source=None): + + mesh = node.data + - if (node.data in self.mesh_cache) and shapename==None: + if (node.data in self.mesh_cache): return self.mesh_cache[mesh] - if (len(node.modifiers) and self.config["use_mesh_modifiers"]) or shapename!=None: - mesh=node.to_mesh(self.scene,True,"RENDER") #is this allright? - else: - mesh=node.data + if (skeyindex==-1 and mesh.shape_keys!=None and len(mesh.shape_keys.key_blocks)): + values=[] + morph_targets=[] + md=None + for k in range(0,len(mesh.shape_keys.key_blocks)): + shape = node.data.shape_keys.key_blocks[k] + values+=[shape.value] #save value + shape.value=0 + + mid = self.new_id("morph") + + for k in range(0,len(mesh.shape_keys.key_blocks)): + + shape = node.data.shape_keys.key_blocks[k] + node.show_only_shape_key=True + node.active_shape_key_index = k + shape.value = 1.0 + mesh.update() + """ + oldval = shape.value + shape.value = 1.0 + + """ + p = node.data + v = node.to_mesh(bpy.context.scene, True, "RENDER") + node.data = v +# self.export_node(node,il,shape.name) + node.data.update() + if (armature and k==0): + md=self.export_mesh(node,armature,k,mid) + else: + md=self.export_mesh(node,None,k) + + node.data = p + node.data.update() + shape.value = 0.0 + morph_targets.append(md) + + """ + shape.value = oldval + """ + node.show_only_shape_key=False + node.active_shape_key_index = 0 + + + self.writel(S_MORPH,1,'<controller id="'+mid+'" name="">') + #if ("skin_id" in morph_targets[0]): + # self.writel(S_MORPH,2,'<morph source="#'+morph_targets[0]["skin_id"]+'" method="NORMALIZED">') + #else: + self.writel(S_MORPH,2,'<morph source="#'+morph_targets[0]["id"]+'" method="NORMALIZED">') + + self.writel(S_MORPH,3,'<source id="'+mid+'-morph-targets">') + self.writel(S_MORPH,4,'<IDREF_array id="'+mid+'-morph-targets-array" count="'+str(len(morph_targets)-1)+'">') + marr="" + warr="" + for i in range(len(morph_targets)): + if (i==0): + continue + elif (i>1): + marr+=" " + + if ("skin_id" in morph_targets[i]): + marr+=morph_targets[i]["skin_id"] + else: + marr+=morph_targets[i]["id"] + + warr+=" 0" + + self.writel(S_MORPH,5,marr) + self.writel(S_MORPH,4,'</IDREF_array>') + self.writel(S_MORPH,4,'<technique_common>') + self.writel(S_MORPH,5,'<accessor source="#'+mid+'-morph-targets-array" count="'+str(len(morph_targets)-1)+'" stride="1">') + self.writel(S_MORPH,6,'<param name="MORPH_TARGET" type="IDREF"/>') + self.writel(S_MORPH,5,'</accessor>') + self.writel(S_MORPH,4,'</technique_common>') + self.writel(S_MORPH,3,'</source>') + + self.writel(S_MORPH,3,'<source id="'+mid+'-morph-weights">') + self.writel(S_MORPH,4,'<float_array id="'+mid+'-morph-weights-array" count="'+str(len(morph_targets)-1)+'" >') + self.writel(S_MORPH,5,warr) + self.writel(S_MORPH,4,'</float_array>') + self.writel(S_MORPH,4,'<technique_common>') + self.writel(S_MORPH,5,'<accessor source="#'+mid+'-morph-weights-array" count="'+str(len(morph_targets)-1)+'" stride="1">') + self.writel(S_MORPH,6,'<param name="MORPH_WEIGHT" type="float"/>') + self.writel(S_MORPH,5,'</accessor>') + self.writel(S_MORPH,4,'</technique_common>') + self.writel(S_MORPH,3,'</source>') + + self.writel(S_MORPH,3,'<targets>') + self.writel(S_MORPH,4,'<input semantic="MORPH_TARGET" source="#'+mid+'-morph-targets"/>') + self.writel(S_MORPH,4,'<input semantic="MORPH_WEIGHT" source="#'+mid+'-morph-weights"/>') + self.writel(S_MORPH,3,'</targets>') + self.writel(S_MORPH,2,'</morph>') + self.writel(S_MORPH,1,'</controller>') + if (armature!=None): + + self.armature_for_morph[node]=armature + + meshdata={} + if (armature): + meshdata = morph_targets[0] + meshdata["morph_id"]=mid + else: + meshdata["id"]=morph_targets[0]["id"] + meshdata["morph_id"]=mid + meshdata["material_assign"]=morph_targets[0]["material_assign"] + + + + self.mesh_cache[node.data]=meshdata + return meshdata + + apply_modifiers = len(node.modifiers) and self.config["use_mesh_modifiers"] + + mesh=node.to_mesh(self.scene,apply_modifiers,"RENDER") #is this allright? + + triangulate=self.config["use_triangles"] + if (triangulate): + bm = bmesh.new() + bm.from_mesh(mesh) + bmesh.ops.triangulate(bm, faces=bm.faces) + bm.to_mesh(mesh) + bm.free() + mesh.update(calc_tessface=True) vertices=[] @@ -340,21 +479,33 @@ class DaeExporter: has_uv=False has_uv2=False has_weights=armature!=None - has_colors=False + has_tangents=self.config["use_tangent_arrays"] # could detect.. + has_colors=len(mesh.vertex_colors) mat_assign=[] uv_layer_count=len(mesh.uv_textures) + if (len(mesh.uv_textures)): + try: + mesh.calc_tangents() + except: + print("Warning, blender API is fucked up, not exporting UVs for this object.") + uv_layer_count=0 + mesh.calc_normals_split() + has_tangents=False + + else: + mesh.calc_normals_split() + has_tangents=False - for fi in range(len(mesh.tessfaces)): - f=mesh.tessfaces[fi] + for fi in range(len(mesh.polygons)): + f=mesh.polygons[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] @@ -368,35 +519,42 @@ class DaeExporter: indices = surface_indices[f.material_index] vi=[] - #make triangles always + #vertices always 3 + """ if (len(f.vertices)==3): vi.append(0) vi.append(1) vi.append(2) elif (len(f.vertices)==4): + #todo, should use shortest path 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]] + for lt in range(f.loop_total): + loop_index = f.loop_start + lt + ml = mesh.loops[loop_index] + mv = mesh.vertices[ml.vertex_index] 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] ) ) + for xt in mesh.uv_layers: + v.uv.append( Vector( xt.data[loop_index].uv ) ) + if (has_colors): + v.color = Vector( mesh.vertex_colors[0].data[loop_index].color ) + + v.normal = Vector( ml.normal ) + + if (has_tangents): + v.tangent = Vector( ml.tangent ) + v.bitangent = Vector( ml.bitangent ) - if (f.use_smooth): - v.normal=Vector( mv.normal ) - else: - v.normal=Vector( f.normal ) # if (armature): # v.vertex = node.matrix_world * v.vertex @@ -410,6 +568,7 @@ class DaeExporter: continue; name = node.vertex_groups[vg.group].name if (name in si["bone_index"]): + #could still put the weight as 0.0001 maybe 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) @@ -418,21 +577,22 @@ class DaeExporter: tup = v.get_tup() idx = 0 - if (tup in vertex_map): + if (skeyindex==-1 and tup in vertex_map): #do not optmize if using shapekeys idx = vertex_map[tup] else: idx = len(vertices) vertices.append(v) vertex_map[tup]=idx - indices.append(idx) + vi.append(idx) - if shapename != None: - meshid = self.new_id("mesh_"+shapename) - self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'_'+shapename+'">') - else: - meshid = self.new_id("mesh") - self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'">') + if (len(vi)>2): + #only triangles and above + indices.append(vi) + + + meshid = self.new_id("mesh") + self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'">') self.writel(S_GEOM,2,'<mesh>') @@ -468,6 +628,37 @@ class DaeExporter: self.writel(S_GEOM,4,'</technique_common>') self.writel(S_GEOM,3,'</source>') + if (has_tangents): + self.writel(S_GEOM,3,'<source id="'+meshid+'-tangents">') + float_values="" + for v in vertices: + float_values+=" "+str(v.tangent.x)+" "+str(v.tangent.y)+" "+str(v.tangent.z) + self.writel(S_GEOM,4,'<float_array id="'+meshid+'-tangents-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+'-tangents-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>') + + self.writel(S_GEOM,3,'<source id="'+meshid+'-bitangents">') + float_values="" + for v in vertices: + float_values+=" "+str(v.bitangent.x)+" "+str(v.bitangent.y)+" "+str(v.bitangent.z) + self.writel(S_GEOM,4,'<float_array id="'+meshid+'-bitangents-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+'-bitangents-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): @@ -475,7 +666,12 @@ class DaeExporter: 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) + try: + float_values+=" "+str(v.uv[uvi].x)+" "+str(v.uv[uvi].y) + except: + # I don't understand this weird multi-uv-layer API, but with this it seems to works + float_values+=" 0 0 " + 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">') @@ -485,84 +681,129 @@ class DaeExporter: self.writel(S_GEOM,4,'</technique_common>') self.writel(S_GEOM,3,'</source>') + # Color Arrays + + if (has_colors): + self.writel(S_GEOM,3,'<source id="'+meshid+'-colors">') + float_values="" + for v in vertices: + float_values+=" "+str(v.color.x)+" "+str(v.color.y)+" "+str(v.color.z) + self.writel(S_GEOM,4,'<float_array id="'+meshid+'-colors-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+'-colors-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>') + # 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>') + prim_type="" + if (triangulate): + prim_type="triangles" + else: + prim_type="polygons" + + 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 + self.writel(S_GEOM,3,'<'+prim_type+' count="'+str(int(len(indices)))+'" 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,3,'<'+prim_type+' count="'+str(int(len(indices)))+'">') # 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 + self.writel(S_GEOM,4,'<input semantic="NORMAL" source="#'+meshid+'-normals" offset="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 + self.writel(S_GEOM,4,'<input semantic="TEXCOORD" source="#'+meshid+'-texcoord-'+str(uvi)+'" offset="0" set="'+str(uvi)+'"/>') + + if (has_colors): + self.writel(S_GEOM,4,'<input semantic="COLOR" source="#'+meshid+'-colors" offset="0"/>') + if (has_tangents): + self.writel(S_GEOM,4,'<input semantic="TEXTANGENT" source="#'+meshid+'-tangents" offset="0"/>') + self.writel(S_GEOM,4,'<input semantic="TEXBINORMAL" source="#'+meshid+'-bitangents" offset="0"/>') + + if (triangulate): + int_values="<p>" + for p in indices: + for i in p: + int_values+=" "+str(i) + int_values+=" </p>" + self.writel(S_GEOM,4,int_values) + else: + for p in indices: + int_values="<p>" + for i in p: + int_values+=" "+str(i) + int_values+=" </p>" + self.writel(S_GEOM,4,int_values) - 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,3,'</'+prim_type+'>') self.writel(S_GEOM,2,'</mesh>') self.writel(S_GEOM,1,'</geometry>') + meshdata={} meshdata["id"]=meshid meshdata["material_assign"]=mat_assign - self.mesh_cache[node.data]=meshdata + if (skeyindex==-1): + self.mesh_cache[node.data]=meshdata # Export armature data (if armature exists) - if (armature!=None): + if (armature!=None and (skel_source!=None or skeyindex==-1)): 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>') + self.writel(S_SKIN,1,'<controller id="'+contid+'">') + if (skel_source!=None): + self.writel(S_SKIN,2,'<skin source="#'+skel_source+'">') + else: + self.writel(S_SKIN,2,'<skin source="#'+meshid+'">') + + self.writel(S_SKIN,3,'<bind_shape_matrix>'+strmtx(node.matrix_world)+'</bind_shape_matrix>') #Joint Names - self.writel(S_CONT,3,'<source id="'+contid+'-joints">') + self.writel(S_SKIN,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>') + self.writel(S_SKIN,4,'<Name_array id="'+contid+'-joints-array" count="'+str(len(si["bone_names"]))+'">'+name_values+'</Name_array>') + self.writel(S_SKIN,4,'<technique_common>') + self.writel(S_SKIN,4,'<accessor source="#'+contid+'-joints-array" count="'+str(len(si["bone_names"]))+'" stride="1">') + self.writel(S_SKIN,5,'<param name="JOINT" type="Name"/>') + self.writel(S_SKIN,4,'</accessor>') + self.writel(S_SKIN,4,'</technique_common>') + self.writel(S_SKIN,3,'</source>') #Pose Matrices! - self.writel(S_CONT,3,'<source id="'+contid+'-bind_poses">') + self.writel(S_SKIN,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>') + self.writel(S_SKIN,4,'<float_array id="'+contid+'-bind_poses-array" count="'+str(len(si["bone_bind_poses"])*16)+'">'+pose_values+'</float_array>') + self.writel(S_SKIN,4,'<technique_common>') + self.writel(S_SKIN,4,'<accessor source="#'+contid+'-bind_poses-array" count="'+str(len(si["bone_bind_poses"]))+'" stride="16">') + self.writel(S_SKIN,5,'<param name="TRANSFORM" type="float4x4"/>') + self.writel(S_SKIN,4,'</accessor>') + self.writel(S_SKIN,4,'</technique_common>') + self.writel(S_SKIN,3,'</source>') #Skin Weights! - self.writel(S_CONT,3,'<source id="'+contid+'-skin_weights">') + self.writel(S_SKIN,3,'<source id="'+contid+'-skin_weights">') skin_weights="" skin_weights_total=0 for v in vertices: @@ -570,22 +811,22 @@ class DaeExporter: 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"/>') + self.writel(S_SKIN,4,'<float_array id="'+contid+'-skin_weights-array" count="'+str(skin_weights_total)+'">'+skin_weights+'</float_array>') + self.writel(S_SKIN,4,'<technique_common>') + self.writel(S_SKIN,4,'<accessor source="#'+contid+'-skin_weights-array" count="'+str(skin_weights_total)+'" stride="1">') + self.writel(S_SKIN,5,'<param name="WEIGHT" type="float"/>') + self.writel(S_SKIN,4,'</accessor>') + self.writel(S_SKIN,4,'</technique_common>') + self.writel(S_SKIN,3,'</source>') + + + self.writel(S_SKIN,3,'<joints>') + self.writel(S_SKIN,4,'<input semantic="JOINT" source="#'+contid+'-joints"/>') + self.writel(S_SKIN,4,'<input semantic="INV_BIND_MATRIX" source="#'+contid+'-bind_poses"/>') + self.writel(S_SKIN,3,'</joints>') + self.writel(S_SKIN,3,'<vertex_weights count="'+str(len(vertices))+'">') + self.writel(S_SKIN,4,'<input semantic="JOINT" source="#'+contid+'-joints" offset="0"/>') + self.writel(S_SKIN,4,'<input semantic="WEIGHT" source="#'+contid+'-skin_weights" offset="1"/>') vcounts="" vs="" vcount=0 @@ -595,20 +836,20 @@ class DaeExporter: 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_SKIN,4,'<vcount>'+vcounts+'</vcount>') + self.writel(S_SKIN,4,'<v>'+vs+'</v>') + self.writel(S_SKIN,3,'</vertex_weights>') - self.writel(S_CONT,2,'</skin>') - self.writel(S_CONT,1,'</controller>') + self.writel(S_SKIN,2,'</skin>') + self.writel(S_SKIN,1,'</controller>') meshdata["skin_id"]=contid return meshdata - def export_mesh_node(self,node,il,shapename=None): + def export_mesh_node(self,node,il): if (node.data==None): return @@ -618,14 +859,19 @@ class DaeExporter: if (node.parent.type=="ARMATURE"): armature=node.parent - meshdata = self.export_mesh(node,armature,shapename) + meshdata = self.export_mesh(node,armature) + close_controller=False - if (armature==None): - self.writel(S_NODES,il,'<instance_geometry url="#'+meshdata["id"]+'">') - else: + if ("skin_id" in meshdata): + close_controller=True 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>') + elif ("morph_id" in meshdata): + self.writel(S_NODES,il,'<instance_controller url="#'+meshdata["morph_id"]+'">') + close_controller=True + elif (armature==None): + self.writel(S_NODES,il,'<instance_geometry url="#'+meshdata["id"]+'">') if (len(meshdata["material_assign"])>0): @@ -638,17 +884,17 @@ class DaeExporter: 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: + if (close_controller): self.writel(S_NODES,il,'</instance_controller>') + else: + self.writel(S_NODES,il,'</instance_geometry>') 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) + bonesid = si["id"]+"-"+str(boneidx) si["bone_index"][bone.name]=boneidx si["bone_ids"][bone]=boneid si["bone_names"].append(bonesid) @@ -677,7 +923,7 @@ class DaeExporter: 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 } + self.skeleton_info[node]={ "bone_count":0, "id":self.new_id("skelbones"),"name":node.name, "bone_index":{},"bone_ids":{},"bone_names":[],"bone_bind_poses":[],"skeleton_nodes":[],"armature_xform":node.matrix_world } @@ -733,7 +979,7 @@ class DaeExporter: 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,2,'<optics>') self.writel(S_LAMPS,3,'<technique_common>') if (light.type=="POINT"): @@ -761,7 +1007,7 @@ class DaeExporter: self.writel(S_LAMPS,3,'</technique_common>') - self.writel(S_LAMPS,2,'</optics>') + #self.writel(S_LAMPS,2,'</optics>') self.writel(S_LAMPS,1,'</light>') @@ -906,21 +1152,18 @@ class DaeExporter: - def export_node(self,node,il,shapename=None): + def export_node(self,node,il): if (not self.is_node_valid(node)): return bpy.context.scene.objects.active = node - if shapename != None: - self.writel(S_NODES,il,'<node id="'+self.validate_id(node.name + '_' + shapename)+'" name="'+node.name+'_'+shapename+'" type="NODE">') - else: - self.writel(S_NODES,il,'<node id="'+self.validate_id(node.name)+'" name="'+node.name+'" type="NODE">') + 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,shapename) + self.export_mesh_node(node,il) elif (node.type=="CURVE"): self.export_curve_node(node,il) elif (node.type=="ARMATURE"): @@ -931,22 +1174,9 @@ class DaeExporter: self.export_lamp_node(node,il) self.valid_nodes.append(node) - if shapename==None: - for x in node.children: - self.export_node(x,il) - if node.type=="MESH" and self.config["export_shapekeys"]: - for k in range(0,len(node.data.shape_keys.key_blocks)): - shape = node.data.shape_keys.key_blocks[k] - oldval = shape.value - shape.value = 1.0 - node.active_shape_key_index = k - p = node.data - v = node.to_mesh(bpy.context.scene, True, "RENDER") - node.data = v - self.export_node(node,il,shape.name) - node.data = p - node.data.update() - shape.value = oldval + for x in node.children: + self.export_node(x,il) + il-=1 self.writel(S_NODES,il,'</node>') @@ -997,18 +1227,22 @@ class DaeExporter: self.writel(S_ASSET,0,'</asset>') - def export_animation_transform_channel(self,target,transform_keys): + def export_animation_transform_channel(self,target,keys,matrices=True): - frame_total=len(transform_keys) + frame_total=len(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: + for k in keys: source_frames += " "+str(k[0]) - source_transforms += " "+strmtx(k[1]) + if (matrices): + source_transforms += " "+strmtx(k[1]) + else: + source_transforms += " "+str(k[1]) + source_interps +=" LINEAR" @@ -1016,27 +1250,38 @@ class DaeExporter: 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,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>') + if (matrices): + # 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>') + else: + # Value 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)+'">'+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="1">') + self.writel(S_ANIM,5,'<param name="X" type="float"/>') + 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,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>') @@ -1047,7 +1292,10 @@ class DaeExporter: 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"/>') + if (matrices): + self.writel(S_ANIM,2,'<channel source="#'+anim_id+'-sampler" target="'+target+'/transform"/>') + else: + self.writel(S_ANIM,2,'<channel source="#'+anim_id+'-sampler" target="'+target+'"/>') self.writel(S_ANIM,1,'</animation>') return [anim_id] @@ -1059,6 +1307,8 @@ class DaeExporter: #Collada starts from 0, blender usually from 1 #The last frame must be included also + frame_orig = self.scene.frame_current + frame_len = 1.0 / self.scene.render.fps frame_total = end - start + 1 frame_sub = 0 @@ -1067,6 +1317,7 @@ class DaeExporter: tcn = [] xform_cache={} + blend_cache={} # Change frames first, export objects last # This improves performance enormously @@ -1081,9 +1332,26 @@ class DaeExporter: if (not node in self.valid_nodes): continue if (allowed!=None and not (node in allowed)): - continue + if (node.type=="MESH" and node.data!=None and (node in self.armature_for_morph) and (self.armature_for_morph[node] in allowed)): + pass #all good you pass with flying colors for morphs inside of action + else: + continue + if (node.type=="MESH" and node.data!=None and node.data.shape_keys!=None and (node.data in self.mesh_cache) and len(node.data.shape_keys.key_blocks)): + target = self.mesh_cache[node.data]["morph_id"] + for i in range(len(node.data.shape_keys.key_blocks)): + + if (i==0): + continue + + name=target+"-morph-weights("+str(i-1)+")" + if (not (name in blend_cache)): + blend_cache[name]=[] + + blend_cache[name].append( (key,node.data.shape_keys.key_blocks[i].value) ) + 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): @@ -1100,6 +1368,7 @@ class DaeExporter: if (node.type=="ARMATURE"): #All bones exported for now + for bone in node.data.bones: bone_name=self.skeleton_info[node]["bone_ids"][bone] @@ -1126,10 +1395,13 @@ class DaeExporter: xform_cache[bone_name].append( (key,mtx) ) + self.scene.frame_set(frame_orig) #export animation xml for nid in xform_cache: - tcn+=self.export_animation_transform_channel(nid,xform_cache[nid]) + tcn+=self.export_animation_transform_channel(nid,xform_cache[nid],True) + for nid in blend_cache: + tcn+=self.export_animation_transform_channel(nid,blend_cache[nid],False) return tcn @@ -1141,10 +1413,19 @@ class DaeExporter: if (self.config["use_anim_action_all"] and len(self.skeletons)): + cached_actions = {} + + for s in self.skeletons: + if s.animation_data and s.animation_data.action: + cached_actions[s] = s.animation_data.action.name + + self.writel(S_ANIM_CLIPS,0,'<library_animation_clips>') for x in bpy.data.actions[:]: - if x in self.action_constraints: + if x.users==0 or x in self.action_constraints: + continue + if (self.config["use_anim_skip_noexp"] and x.name.endswith("-noexp")): continue bones=[] @@ -1179,12 +1460,19 @@ class DaeExporter: 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,2,'<instance_animation url="#'+z+'"/>') self.writel(S_ANIM_CLIPS,1,'</animation_clip>') self.writel(S_ANIM_CLIPS,0,'</library_animation_clips>') + for s in self.skeletons: + if (s.animation_data==None): + continue + if s in cached_actions: + s.animation_data.action = bpy.data.actions[cached_actions[s]] + else: + s.animation_data.action = None else: self.export_animation(self.scene.frame_start,self.scene.frame_end) @@ -1207,6 +1495,19 @@ class DaeExporter: self.export_scene() self.writel(S_GEOM,0,'</library_geometries>') + + #morphs always go before skin controllers + if S_MORPH in self.sections: + for l in self.sections[S_MORPH]: + self.writel(S_CONT,0,l) + del self.sections[S_MORPH] + + #morphs always go before skin controllers + if S_SKIN in self.sections: + for l in self.sections[S_SKIN]: + self.writel(S_CONT,0,l) + del self.sections[S_SKIN] + self.writel(S_CONT,0,'</library_controllers>') self.writel(S_CAMS,0,'</library_cameras>') self.writel(S_LAMPS,0,'</library_lights>') @@ -1253,6 +1554,7 @@ class DaeExporter: self.skeleton_info={} self.config=kwargs self.valid_nodes=[] + self.armature_for_morph={} |