summaryrefslogtreecommitdiff
path: root/tools/export
diff options
context:
space:
mode:
Diffstat (limited to 'tools/export')
-rw-r--r--tools/export/blender25/install.txt16
-rw-r--r--tools/export/blender25/io_scene_dae/__init__.py32
-rw-r--r--tools/export/blender25/io_scene_dae/export_dae.py602
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={}