Commits

Cedric Pinson  committed fe4bd43 Merge

merge to last version of osg

  • Participants
  • Parent commits 4078f11, b41fa3a

Comments (0)

Files changed (3)

File blenderExporter/osg/__init__.py

 import osgconf
 
-VERSION = "0.8.0"
+VERSION = "0.9.0"
 AUTHOR  = "Jeremy Moles, Cedric Pinson"
-EMAIL   = "jeremy@emperorlinux.com, mornifle@plopbyte.net"
+EMAIL   = "jeremy@emperorlinux.com, cedric.pinson@plopbyte.net"
 URL     = "http://www.plopbyte.net, http://hg.plopbyte.net/osgexport"
 
 DOC = """

File blenderExporter/osg/osgdata.py

             return o
     return None
 
-def createAnimationGenericObject(osg_object, blender_object, config, UpdateCallback):
+def createAnimationGenericObject(osg_object, blender_object, config, update_callback):
     if config.export_anim is not True:
         return None
 
         ipo2animation = BlenderIpoOrActionToAnimation(ipo = ipo, config = config)
         anim = ipo2animation.createAnimationFromIpo(blender_object.getName())
 
-        update_callback = UpdateCallback()
         update_callback.setName(osg_object.name)
         osg_object.update_callbacks.append(update_callback)
         return anim
     return None
 
+def createUpdateMatrixTransform():
+    callback = UpdateMatrixTransform()
+    callback.stacked_transforms.append(StackedTranslateElement())
+    callback.stacked_transforms.append(StackedRotateAxisElement(name = 'euler_z', axis = Vector(0,0,1) ))
+    callback.stacked_transforms.append(StackedRotateAxisElement(name = 'euler_y', axis = Vector(0,1,0) ))
+    callback.stacked_transforms.append(StackedRotateAxisElement(name = 'euler_x', axis = Vector(1,0,0) ))
+    callback.stacked_transforms.append(StackedScaleElement())
+    return callback
+
 def createAnimationObjectAndSetCallback(osg_node, obj, config):
-    return createAnimationGenericObject(osg_node, obj, config, UpdateTransform)
+    return createAnimationGenericObject(osg_node, obj, config, createUpdateMatrixTransform())
 
 def createAnimationMaterialAndSetCallback(osg_node, obj, config):
-    return createAnimationGenericObject(osg_node, obj, config, UpdateMaterial)
+    return createAnimationGenericObject(osg_node, obj, config, UpdateMaterial())
 
 
 class Export(object):
             anim = ipo2animation.createAnimationFromIpo(obj.getName())
             self.animations[anim.name] = anim
 
-            update_callback = UpdateTransform()
+            update_callback = UpdateMatrixTransform()
             update_callback.setName(osg_node.name)
             osg_node.update_callbacks.append(update_callback)
 
                 # parent bone to object bone
                 armature = obj.getParent()
                 matrixArmatureInWorldSpace = armature.getMatrix('worldspace')
-                matrixBoneinArmatureSpace = bone.matrix['ARMATURESPACE']
+                matrixBoneinArmatureSpace = bone.bone_matrix['ARMATURESPACE']
                 boneInWorldSpace = matrixBoneinArmatureSpace * matrixArmatureInWorldSpace
                 matrix = getDeltaMatrixFromMatrix(boneInWorldSpace, obj.getMatrix('worldspace'))
                 item.matrix = matrix
 	hasVertexGroup = len(mesh.getData(False, True).getVertGroupNames()) != 0
 
         geometries = []
-        if exportInfluence is False or hasVertexGroup is False:
-            converter = BlenderObjectToGeometry(object = mesh, config = self.config, uniq_stateset = self.uniq_stateset)
-            geometries = converter.convert()
+        converter = BlenderObjectToGeometry(object = mesh, config = self.config, uniq_stateset = self.uniq_stateset)
+        sources_geometries = converter.convert()
+
+        if exportInfluence is True and hasVertexGroup is True:
+            for geom in sources_geometries:
+                rig_geom = RigGeometry()
+                rig_geom.sourcegeometry = geom
+                rig_geom.copyFrom(geom)
+                rig_geom.groups = geom.groups
+                geometries.append(rig_geom)
         else:
-            converter = BlenderObjectToRigGeometry(object = mesh, config = self.config, uniq_stateset = self.uniq_stateset)
-            geometries = converter.convert()
+            geometries = sources_geometries
+
         if len(geometries) > 0:
             for geom in geometries:
                 geode.drawables.append(geom)
         return 0
 
     def createGeomForMaterialIndex(self, material_index, mesh):
-        geom = self.geom_type()
+        geom = Geometry()
+        geom.groups = {}
         if (len(mesh.faces) == 0):
             log("object %s has no faces, so no materials" % self.object.getName())
             return None
             )
 
         channels         = []
-        channel_times    = {'Rotation': set(), 'Translation': set(), 'Scale': set(), 'Color' : set() }
-        channel_names    = {'Rotation': 'rotation', 'Translation': 'position', 'Scale': 'scale', 'Color' : 'color'}
-        channel_samplers = {'Rotation': None, 'Translation': None, 'Scale': None, 'Color' : None}
-        channel_ipos     = {'Rotation': [], 'Translation': [], 'Scale': [], 'Color': []}
+        channel_times    = {'EulerX': set(), 'EulerY': set(), 'EulerZ': set(), 'Rotation': set(), 'Translation': set(), 'Scale': set(), 'Color' : set() }
+        channel_names    = {'EulerX': 'euler_x', 'EulerY': 'euler_y', 'EulerZ': 'euler_z', 'Rotation': 'rotation', 'Translation': 'translate', 'Scale': 'scale', 'Color' : 'color'}
+        channel_samplers = {'EulerX': None, 'EulerY': None, 'EulerZ': None, 'Rotation': None, 'Translation': None, 'Scale': None, 'Color' : None}
+        channel_ipos     = {'EulerX': [], 'EulerY': [], 'EulerZ': [], 'Rotation': [], 'Translation': [], 'Scale': [], 'Color': []}
         duration = 0
 
         for curve in ipo:
                 if DEBUG: debug("ipo %s curve %s not supported" % (ipo.getName(), curve.name))
                 continue
 
-            elif curve.name == "RotX" or curve.name == "RotY" or curve.name == "RotZ" or curve.name == "QuatX" or curve.name == "QuatY" or curve.name == "QuatZ" or curve.name == "QuatW":
+            elif curve.name == "QuatX" or curve.name == "QuatY" or curve.name == "QuatZ" or curve.name == "QuatW":
                 times = channel_times['Rotation']
                 channel_ipos['Rotation'].append(curve)
 
+            elif curve.name == "RotX":
+                times = channel_times['EulerX']
+                channel_ipos['EulerX'].append(curve)
+
+            elif curve.name == "RotY":
+                times = channel_times['EulerY']
+                channel_ipos['EulerY'].append(curve)
+
+            elif curve.name == "RotZ":
+                times = channel_times['EulerZ']
+                channel_ipos['EulerZ'].append(curve)
+
             elif curve.name == "LocX" or curve.name == "LocY" or curve.name == "LocZ":
                 times = channel_times['Translation']
                 channel_ipos['Translation'].append(curve)
                         trans[2] = val
                     elif curve.name == 'QuatW':
                         quat.w = val
-                        rtype  = "Quat"
                     elif curve.name == 'QuatX':
                         quat.x = val
-                        rtype  = "Quat"
                     elif curve.name == 'QuatY':
                         quat.y = val
-                        rtype  = "Quat"
                     elif curve.name == 'QuatZ':
                         quat.z = val
-                        rtype  = "Quat"
                     elif curve.name == 'ScaleX':
                         scale[0] = val
                     elif curve.name == 'ScaleY':
                         scale[2] = val
                     elif curve.name == 'RotX':
                         rot.x = val * 10
-                        rtype = "Euler"
                     elif curve.name == 'RotY':
                         rot.y = val * 10
-                        rtype = "Euler"
                     elif curve.name == 'RotZ':
                         rot.z = val * 10
-                        rtype = "Euler"
                     elif curve.name == 'R':
                         color[0] = val
                     elif curve.name == 'G':
                     channel_samplers[key].setName("scale")
 
                 elif key == 'Rotation':
-                    if rtype == "Quat":
-                        quat.normalize()
-                        channel_samplers[key].keys.append((realtime, quat.x, quat.y, quat.z, quat.w))
-                        channel_samplers[key].type = "QuatSphericalLinearChannel"
-                        channel_samplers[key].setName("quaternion")
+                    quat.normalize()
+                    channel_samplers[key].keys.append((realtime, quat.x, quat.y, quat.z, quat.w))
+                    channel_samplers[key].type = "QuatSphericalLinearChannel"
+                    channel_samplers[key].setName("quaternion")
 
-                    elif rtype == "Euler":
-                        channel_samplers[key].keys.append((realtime, math.radians(rot.x)  , math.radians(rot.y), math.radians(rot.z) ))
-                        channel_samplers[key].type = "Vec3LinearChannel"
-                        channel_samplers[key].setName("euler")
+                elif key == 'EulerX':
+                    channel_samplers[key].keys.append((realtime, math.radians(rot.x)))
+                    channel_samplers[key].type = "FloatLinearChannel"
+                    channel_samplers[key].setName("euler_x")
+
+                elif key == 'EulerY':
+                    channel_samplers[key].keys.append((realtime, math.radians(rot.y)))
+                    channel_samplers[key].type = "FloatLinearChannel"
+                    channel_samplers[key].setName("euler_y")
+
+                elif key == 'EulerZ':
+                    channel_samplers[key].keys.append((realtime, math.radians(rot.z) ))
+                    channel_samplers[key].type = "FloatLinearChannel"
+                    channel_samplers[key].setName("euler_z")
 
                 elif key == 'Translation':
                     channel_samplers[key].keys.append((realtime, trans[0], trans[1], trans[2]))
                     channel_samplers[key].type = "Vec3LinearChannel"
-                    channel_samplers[key].setName("position")
+                    channel_samplers[key].setName("translate")
 
                 elif key == 'Color':
                     channel_samplers[key].keys.append((realtime, color[0], color[1], color[2], color[3]))

File blenderExporter/osg/osgobject.py

 
 
 Matrix    = Blender.Mathutils.Matrix
+Vector    = Blender.Mathutils.Vector
 FLOATPRE  = 5
 CONCAT    = lambda s, j="": j.join(str(v) for v in s)
 STRFLT    = lambda f: "%%.%df" % FLOATPRE % float(f)
                 return found
     return None
 
+
 class Writer(object):
+    instances = {}
+    wrote_elements = {}
+
     def __init__(self, comment = None):
         object.__init__(self)
         self.comment = comment
         self.indent_level = 0
+        self.counter = len(Writer.instances)
+        Writer.instances[self] = True
 
     def __repr__(self):
         ret = ""
 #        text = self.ascii().replace("\t", "").replace("#", (" " * INDENT)).replace("$", (" " * (INDENT*self.indent_level) ))
-        text = Object.writeInstanceOrUseIt(self).replace("\t", "").replace("#", (" " * INDENT)).replace("$", (" " * (INDENT*self.indent_level) ))
+        text = Writer.writeInstanceOrUseIt(self).replace("\t", "").replace("#", (" " * INDENT)).replace("$", (" " * (INDENT*self.indent_level) ))
         return ret + text
 
     def __str__(self):
         return self.__repr__()
 
+    @staticmethod
+    def resetWriter():
+        Writer.instances = {}
+        wrote_elements = {}
+
+    @staticmethod
+    def writeInstanceOrUseIt(obj):
+        if Writer.wrote_elements.has_key(obj) and obj.shadow_object is not None:
+            return obj.shadow_object.ascii()
+        Writer.wrote_elements[obj] = True
+        return obj.ascii()
+
 class ShadowObject(Writer):
     def __init__(self, *args, **kwargs):
         Writer.__init__(self, *args, **kwargs)
         self.target = args[0]
     
     def ascii(self):
-        text = "$#Use " + self.target.generateID() + "\n"
+        text = "$Use " + self.target.generateID() + "\n"
         return text
 
+
 class Object(Writer):
-    instances = {}
-    wrote_elements = {}
     
     def __init__(self, *args, **kwargs):
-        Writer.__init__(self, *args, **kwargs)
+        Writer.__init__(self, *args)
+        self.shadow_object = ShadowObject(self)
         self.dataVariance = "UNKNOWN"
-        self.name = "None"
-        self.counter = len(Object.instances)
-        Object.instances[self] = True
+        self.name = kwargs.get('name', "None")
 
-    @staticmethod
-    def resetWriter():
-        Object.instances = {}
-        wrote_elements = {}
-
-    @staticmethod
-    def writeInstanceOrUseIt(obj):
-        if Object.wrote_elements.has_key(obj):
-            shadow_object = ShadowObject(obj)
-            return shadow_object.ascii()
-        Object.wrote_elements[obj] = True
-        return obj.ascii()
+    def copyFrom(self, obj):
+        self.name = obj.name
+        self.dataVariance = obj.dataVariance
 
     def generateID(self):
         return "uniqid_" + self.className() + "_" + str(self.counter)
             text += "$#name \"%s\"\n" % self.name
         return text
 
-class UpdateTransform(Object):
+class UpdateMatrixTransform(Object):
     def __init__(self, *args, **kwargs):
         Object.__init__(self, *args, **kwargs)
+        self.stacked_transforms = []
 
     def className(self):
-        return "UpdateTransform"
+        return "UpdateMatrixTransform"
 
     def ascii(self):
         text = "$osgAnimation::%s {\n" % self.className()
         text += Object.printContent(self)
+        text += self.printContent()
         text += "$}\n"
         return text
 
+    def printContent(self):
+        text =""
+        for s in self.stacked_transforms:
+            s.indent_level = self.indent_level + 1
+            text += str(s)
+        return text
+
 class UpdateMaterial(Object):
     def __init__(self, *args, **kwargs):
         Object.__init__(self, *args, **kwargs)
         text += "$}\n"
         return text
 
-class UpdateBone(Object):
+class StackedMatrixElement(Object):
     def __init__(self, *args, **kwargs):
         Object.__init__(self, *args, **kwargs)
+        if self.name == "None":
+            self.name = "matrix"
+        self.matrix = kwargs.get('matrix', Matrix().resize4x4().identity())
+
+    def className(self):
+        return "StackedMatrixElement"
+
+    def ascii(self):
+        text = "$osgAnimation::%s {\n" % self.className()
+        text += Object.printContent(self)
+        text += self.printContent()
+        text += "$}\n"
+        return text
+
+    def generateID(self):
+        return None
+
+    def printContent(self):
+        text = "$#Matrix {\n"
+        for i in range(0,4):
+            text += "$##%s %s %s %s\n" % (STRFLT(self.matrix[i][0]), STRFLT(self.matrix[i][1]),STRFLT(self.matrix[i][2]), STRFLT(self.matrix[i][3]))
+        text += "$#}\n"
+        return text
+
+class StackedTranslateElement(Object):
+    def __init__(self, *args, **kwargs):
+        Object.__init__(self, *args, **kwargs)
+        self.translate = Vector(0,0,0)
+        self.name = "translate"
+
+    def className(self):
+        return "StackedTranslateElement"
+
+    def ascii(self):
+        text = "$osgAnimation::%s {\n" % self.className()
+        text += Object.printContent(self)
+        text += "$}\n"
+        return text
+
+    def generateID(self):
+        return None
+
+    def printContent(self):
+        text = "$#translate %s %s %s\n" % (STRFLT(self.translate[0]), STRFLT(self.translate[1]),STRFLT(self.translate[2]))
+        return text
+
+class StackedScaleElement(Object):
+    def __init__(self, *args, **kwargs):
+        Object.__init__(self, *args, **kwargs)
+        self.scale = Vector(1,1,1)
+        self.name = "scale"
+
+    def className(self):
+        return "StackedScaleElement"
+
+    def ascii(self):
+        text = "$osgAnimation::%s {\n" % self.className()
+        text += Object.printContent(self)
+        text += "$}\n"
+        return text
+
+    def generateID(self):
+        return None
+
+    def printContent(self):
+        text = "$#scale %s %s %s\n" % (STRFLT(self.scale[0]), STRFLT(self.scale[1]),STRFLT(self.scale[2]))
+        return text
+
+class StackedRotateAxisElement(Object):
+    def __init__(self, *args, **kwargs):
+        Object.__init__(self, *args, **kwargs)
+        self.axis = kwargs.get('axis', Vector(1,0,0))
+        self.angle = kwargs.get('angle', 0)
+
+    def className(self):
+        return "StackedRotateAxisElement"
+
+    def ascii(self):
+        text = "$osgAnimation::%s {\n" % self.className()
+        text += Object.printContent(self)
+        text += "$}\n"
+        return text
+
+    def generateID(self):
+        return None
+
+    def printContent(self):
+        text = "$#axis %s %s %s\n" % (STRFLT(self.axis[0]), STRFLT(self.axis[1]),STRFLT(self.axis[2]))
+        text = "$#angle %s\n" % (STRFLT(self.angle))
+        return text
+
+class StackedQuaternionElement(Object):
+    def __init__(self, *args, **kwargs):
+        Object.__init__(self, *args, **kwargs)
+        self.quaternion = Matrix().resize4x4().identity().toQuat()
+        self.name = "quaternion"
+
+    def className(self):
+        return "StackedQuaternionElement"
+
+    def ascii(self):
+        text = "$osgAnimation::%s {\n" % self.className()
+        text += Object.printContent(self)
+        text += "$}\n"
+        return text
+
+    def generateID(self):
+        return None
+
+    def printContent(self):
+        text = "$#quaternion %s %s %s %s\n" % (STRFLT(self.quaternion.x(), STRFLT(self.quaternion.y()),STRFLT(self.quaternion.z()),STRFLT(self.quaternion.w()) ))
+        return text
+
+class UpdateBone(UpdateMatrixTransform):
+    def __init__(self, *args, **kwargs):
+        UpdateMatrixTransform.__init__(self, *args, **kwargs)
 
     def className(self):
         return "UpdateBone"
     def ascii(self):
         text = "$osgAnimation::%s {\n" % self.className()
         text += Object.printContent(self)
+        text += UpdateMatrixTransform.printContent(self)
         text += "$}\n"
         return text
 
                 text += "$#}\n"
         return text
 
-class VertexArray(Object):
+class ShadowVertexArrayObject(Writer):
     def __init__(self, *args, **kwargs):
-        Object.__init__(self, *args, **kwargs)
+        Writer.__init__(self)
+        self.target = args[0]
+        self.binding = None
+    
+    def ascii(self):
+        text = ""
+        if self.binding is not None:
+            text += "$" + self.binding + "\n"
+        text += "$" + self.target.className() + " Use " + self.target.generateID() + "\n"
+        return text
+
+class VertexArray(Writer):
+    def __init__(self, *args, **kwargs):
+        Writer.__init__(self)
         self.array = kwargs.get('array', [])
+        self.shadow_object = ShadowVertexArrayObject(self, "Vec3Array")
 
     def className(self):
         return "VertexArray"
 
+    def generateID(self):
+        return self.className() + "_" + str(self.counter)
+
     def ascii(self):
-        text = "$%s %s {\n" % (self.className(), str(len(self.array)))
+        text = "$%s UniqueID %s Vec3Array %s\n${\n" % (self.className(), self.generateID(), str(len(self.array)))
         for i in self.array:
                 text += "$#%s %s %s\n" % (STRFLT(i[0]), STRFLT(i[1]), STRFLT(i[2]))
         text += "$}\n"
         return text
 
-class NormalArray(Writer):
+
+class NormalArray(VertexArray):
     def __init__(self, *args, **kwargs):
-        Writer.__init__(self, *args, **kwargs)
-        self.array = []
+        VertexArray.__init__(self, *args, **kwargs)
+        self.shadow_object = ShadowVertexArrayObject(self, "Vec3Array")
+        self.shadow_object.binding = "NormalBinding PER_VERTEX"
 
     def className(self):
         return "NormalArray"
 
     def ascii(self):
         text = "$NormalBinding PER_VERTEX\n"
-        text += "$%s %s {\n" % (self.className(), len(self.array))
+        text += "$%s UniqueID %s Vec3Array %s\n${\n" % (self.className(), self.generateID(), len(self.array))
         for i in self.array:
                 text += "$#%s %s %s\n" % (STRFLT(i[0]), STRFLT(i[1]), STRFLT(i[2]))
         text += "$}\n"
         return text
 
-class ColorArray(Writer):
+class ColorArray(VertexArray):
     def __init__(self, *args, **kwargs):
-        Writer.__init__(self, *args, **kwargs)
-        self.array = []
+        VertexArray.__init__(self, *args, **kwargs)
+        self.shadow_object = ShadowVertexArrayObject(self, "Vec4Array")
+        self.shadow_object.binding = "ColorBinding PER_VERTEX"
 
     def className(self):
         return "ColorArray"
 
     def ascii(self):
-        text = "$%s Vec4Array %s {\n" % (self.className(), len(self.array))
+        text = "$%s UniqueID %s Vec4Array %s\n${\n" % (self.className(), self.generateID(), len(self.array))
         for i in self.array:
                 text += "$#%s %s %s %s\n" % (STRFLT(i[0]), STRFLT(i[1]), STRFLT(i[2]), STRFLT(i[3]))
         text += "$}\n"
         return text
 
-class TexCoordArray(Writer):
+class TexCoordArray(VertexArray):
     def __init__(self, *args, **kwargs):
-        Writer.__init__(self, *args, **kwargs)
-        self.array = []
+        VertexArray.__init__(self, *args, **kwargs)
         self.index = 0
+        self.shadow_object = ShadowVertexArrayObject(self, "Vec2Array")
 
     def className(self):
         return "TexCoordArray"
 
     def ascii(self):
-        text = "$%s %s Vec2Array %s {\n" % (self.className(), self.index,  len(self.array))
+        text = "$%s %s UniqueID %s Vec2Array %s\n${\n" % (self.className(), self.index, self.generateID(),  len(self.array))
         for i in self.array:
                 text += "$#%s %s\n" % (STRFLT(i[0]), STRFLT(i[1]))
         text += "$}\n"
         Object.__init__(self, *args, **kwargs)
         self.indexes = []
         self.type = None
+        self.shadow_object = None
 
     def getSizeArray(self):
         element = "DrawElementsUByte"
     def className(self):
         return "Geometry"
 
+    def copyFrom(self, geometry):
+        Object.copyFrom(self, geometry)
+        self.primitives = geometry.primitives
+        self.vertexes = geometry.vertexes
+        self.normals = geometry.normals
+        self.colors = geometry.colors
+        self.uvs = geometry.uvs
+        self.stateset = geometry.stateset
+
     def ascii(self):
         text = "$%s {\n" % self.className()
         text += Object.printContent(self)
 
     def printContent(self):
         text = ""
-        if self.stateset is not None:
-            self.stateset.indent_level = self.indent_level + 1
-            text += str(self.stateset)
         if len(self.primitives):
             text += "$#Primitives %s {\n" % (str(len(self.primitives)))
             for i in self.primitives:
         if self.normals:
             self.normals.indent_level = self.indent_level + 1
             text += str(self.normals)
+        if self.colors:
+            self.colors.indent_level = self.indent_level + 1
+            text += str(self.colors)
         for i in self.uvs.values():
             if i:
                 i.indent_level = self.indent_level + 1
                 text += str(i)
-        if self.colors:
-            self.colors.indent_level = self.indent_level + 1
-            text += str(self.colors)
+        if self.stateset is not None:
+            self.stateset.indent_level = self.indent_level + 1
+            text += str(self.stateset)
         return text
 
 ################################## animation node ######################################
-class Bone(Group):
+class Bone(MatrixTransform):
     def __init__(self, skeleton = None, bone = None, parent=None, **kwargs):
-        Group.__init__(self, **kwargs)
+        MatrixTransform.__init__(self, **kwargs)
         self.dataVariance = "DYNAMIC"
         self.parent = parent
         self.skeleton = skeleton
         self.bone = bone
-        self.matrix = {'BONESPACE': Matrix().resize4x4().identity() }
+        self.bone_matrix = {'BONESPACE': Matrix().resize4x4().identity() }
 
     def buildBoneChildren(self):
         if self.skeleton is None or self.bone is None:
         update_callback.setName(self.name)
         self.update_callbacks.append(update_callback)
 
-        self.matrix['BONESPACE'] = self.bone.matrix['BONESPACE'].copy().resize4x4()
+        self.bone_matrix['BONESPACE'] = self.bone.matrix['BONESPACE'].copy().resize4x4()
 
         if self.parent:
-            parent_tail = self.parent.bone.tail['BONESPACE'] * self.parent.matrix['BONESPACE'].rotationPart().copy().invert()
-            parent_head = self.parent.bone.head['BONESPACE'] * self.parent.matrix['BONESPACE'].rotationPart().copy().invert()
+            parent_tail = self.parent.bone.tail['BONESPACE'] * self.parent.bone_matrix['BONESPACE'].rotationPart().copy().invert()
+            parent_head = self.parent.bone.head['BONESPACE'] * self.parent.bone_matrix['BONESPACE'].rotationPart().copy().invert()
 
             pos = parent_tail - parent_head + self.bone.head['BONESPACE']
 
-            self.matrix['BONESPACE'][3][0] = pos[0]
-            self.matrix['BONESPACE'][3][1] = pos[1]
-            self.matrix['BONESPACE'][3][2] = pos[2]
-            self.matrix['ARMATURESPACE']   = self.matrix['BONESPACE'] * self.parent.matrix['ARMATURESPACE']
+            self.bone_matrix['BONESPACE'][3][0] = pos[0]
+            self.bone_matrix['BONESPACE'][3][1] = pos[1]
+            self.bone_matrix['BONESPACE'][3][2] = pos[2]
+            self.bone_matrix['ARMATURESPACE']   = self.bone_matrix['BONESPACE'] * self.parent.bone_matrix['ARMATURESPACE']
 
         else:
             pos = self.bone.head['BONESPACE']
-            self.matrix['BONESPACE'][3][0] = pos[0]
-            self.matrix['BONESPACE'][3][1] = pos[1]
-            self.matrix['BONESPACE'][3][2] = pos[2]
-            self.matrix['ARMATURESPACE']   = self.matrix['BONESPACE'].copy()
+            self.bone_matrix['BONESPACE'][3][0] = pos[0]
+            self.bone_matrix['BONESPACE'][3][1] = pos[1]
+            self.bone_matrix['BONESPACE'][3][2] = pos[2]
+            self.bone_matrix['ARMATURESPACE']   = self.bone_matrix['BONESPACE'].copy()
+
+        # add bind matrix in localspace callback
+        update_callback.stacked_transforms.append(StackedMatrixElement(name = "bindmatrix", matrix = self.bone_matrix['BONESPACE'].copy()))
+        update_callback.stacked_transforms.append(StackedTranslateElement())
+        update_callback.stacked_transforms.append(StackedQuaternionElement())
+        update_callback.stacked_transforms.append(StackedScaleElement())
+
+        self.matrix = self.bone_matrix['BONESPACE'].copy()
 
         if not self.bone.hasChildren():
             return
         text += Object.printContent(self)
         text += Node.printContent(self)
         text += self.printContent()
+        text += MatrixTransform.printContent(self)
         text += Group.printContent(self)
         text += "$}\n"
         return text
 
     def printContent(self):
+        matrix = self.bone_matrix['ARMATURESPACE'].copy()
+        matrix.invert()
+        text = "$#InvBindMatrixInSkeletonSpace {\n"
+        for i in range(0,4):
+            text += "$##%s %s %s %s\n" % (STRFLT(matrix[i][0]), STRFLT(matrix[i][1]),STRFLT(matrix[i][2]), STRFLT(matrix[i][3]))
+        text += "$#}\n"
+        return text
+
         matrix = self.matrix['BONESPACE'].copy()
         text = "$#bindQuaternion %s %s %s %s\n" % (
             STRFLT(matrix.toQuat().x),
         return text
 
 
-class Skeleton(Bone):
+class Skeleton(MatrixTransform):
     def __init__(self, name, matrix):
-        Bone.__init__(self)
+        MatrixTransform.__init__(self)
         self.boneList = []
-        self.matrix['BONESPACE'] = matrix
+        self.matrix = matrix
         self.setName(name)
         self.update_callbacks = []
         self.update_callbacks.append(UpdateSkeleton())
         text = "$osgAnimation::%s {\n" % self.className()
         text += Object.printContent(self)
         text += Node.printContent(self)
-        text += Bone.printContent(self)
+        text += MatrixTransform.printContent(self)
         text += Group.printContent(self)
         text += "$}\n"
         return text
         Geometry.__init__(self, *args, **kwargs)
         self.groups = {}
         self.dataVariance = "DYNAMIC"
+        self.sourcegeometry = None
 
     def className(self):
         return "RigGeometry"
             for name, grp in self.groups.items():
                 grp.indent_level = self.indent_level + 1
                 text += str(grp)
+
+        self.sourcegeometry.indent_level = self.indent_level + 1
+        text += str(self.sourcegeometry)
         return text
 
 class AnimationManagerBase(Object):