Animation exporter: Need your help.

Scripting in Blender with Python, and working on the API

Moderators: jesterKing, stiv

Post Reply
Alkaelis
Posts: 0
Joined: Mon Jan 21, 2013 6:09 pm

Animation exporter: Need your help.

Post by Alkaelis » Mon Jan 21, 2013 6:34 pm

Hi, you may say "an other noob who can't figure out how our api work for animation's export" ... and you may be right !

Anyway, some friends and I are working on a game developement project.
We need entities, so we decided to use blender and we made/used differents exporter for mesh and skeleton, but we still can not export animations.

We spends days on blender's api and even that all what we tried didn't made what we want. So what do we want ?

We need to export transformation 4*4 matrices ( translation, scale and rotation) of each bones for each frames in world space ( or object space ) as a float array.

This is actually where we are, and this is our exporter:
import bpy;
from bpy_extras.io_utils import ExportHelper;
from bpy.props import IntProperty;
from mathutils import Matrix;

bl_info = {
"name": "Spout Animation Model",
"author": "Karang & Alkaelis",
"blender": (2,6,2),
"version": (0,0,1),
"location": "File > Import-Export",
"description": "Export bones animation for spout engine.",
"category": "Import-Export"
}

class ExportSAM(bpy.types.Operator, ExportHelper):
bl_idname = "export_anim.sam";
bl_label = "Spout Animation Model";
bl_options = {'PRESET'};

filename_ext = ".sam";

startFrame_prop = IntProperty(name="Start frame",
description="Starting frame for the animation",
default=1);
endFrame_prop = IntProperty(name="End frame",
description="Ending frame for the animation",
default=250);

def mul(self, mat1, mat2):
return [[mat1[0][0]*mat2[0][0]+mat1[0][1]*mat2[1][0]+mat1[0][2]*mat2[2][0]+mat1[0][3]*mat2[3][0],
mat1[0][0]*mat2[0][1]+mat1[0][1]*mat2[1][1]+mat1[0][2]*mat2[2][1]+mat1[0][3]*mat2[3][1],
mat1[0][0]*mat2[0][2]+mat1[0][1]*mat2[1][2]+mat1[0][2]*mat2[2][2]+mat1[0][3]*mat2[3][2],
mat1[0][0]*mat2[0][3]+mat1[0][1]*mat2[1][3]+mat1[0][2]*mat2[2][3]+mat1[0][3]*mat2[3][3]],
[mat1[1][0]*mat2[0][0]+mat1[1][1]*mat2[1][0]+mat1[1][2]*mat2[2][0]+mat1[1][3]*mat2[3][0],
mat1[1][0]*mat2[0][1]+mat1[1][1]*mat2[1][1]+mat1[1][2]*mat2[2][1]+mat1[1][3]*mat2[3][1],
mat1[1][0]*mat2[0][2]+mat1[1][1]*mat2[1][2]+mat1[1][2]*mat2[2][2]+mat1[1][3]*mat2[3][2],
mat1[1][0]*mat2[0][3]+mat1[1][1]*mat2[1][3]+mat1[1][2]*mat2[2][3]+mat1[1][3]*mat2[3][3]],

[mat1[2][0]*mat2[0][0]+mat1[2][1]*mat2[1][0]+mat1[2][2]*mat2[2][0]+mat1[2][3]*mat2[3][0],
mat1[2][0]*mat2[0][1]+mat1[2][1]*mat2[1][1]+mat1[2][2]*mat2[2][1]+mat1[2][3]*mat2[3][1],
mat1[2][0]*mat2[0][2]+mat1[2][1]*mat2[1][2]+mat1[2][2]*mat2[2][2]+mat1[2][3]*mat2[3][2],
mat1[2][0]*mat2[0][3]+mat1[2][1]*mat2[1][3]+mat1[2][2]*mat2[2][3]+mat1[2][3]*mat2[3][3]],

[mat1[3][0]*mat2[0][0]+mat1[3][1]*mat2[1][0]+mat1[3][2]*mat2[2][0]+mat1[3][3]*mat2[3][0],
mat1[3][0]*mat2[0][1]+mat1[3][1]*mat2[1][1]+mat1[3][2]*mat2[2][1]+mat1[3][3]*mat2[3][1],
mat1[3][0]*mat2[0][2]+mat1[3][1]*mat2[1][2]+mat1[3][2]*mat2[2][2]+mat1[3][3]*mat2[3][2],
mat1[3][0]*mat2[0][3]+mat1[3][1]*mat2[1][3]+mat1[3][2]*mat2[2][3]+mat1[3][3]*mat2[3][3]]];


def getMatrix(self, bone):
if (bone.parent is None):
return bone.matrix_channel;
else:
return self.mul(self.getMatrix(bone.parent), bone.matrix_channel);


def writeAnimation(self, f, scene, startFrame, endFrame, bone):
indent = " ";
for frame in range(startFrame, endFrame+1):
scene.frame_set(frame);
m = self.getMatrix(bone);
f.write(indent + str(frame) + ": ");
f.write("%.6f, %.6f, %.6f, %.6f, " % (m[0][0], m[1][0], m[2][0], m[3][0]));
f.write("%.6f, %.6f, %.6f, %.6f, " % (m[0][1], m[1][1], m[2][1], m[3][1]));
f.write("%.6f, %.6f, %.6f, %.6f, " % (m[0][2], m[1][2], m[2][2], m[3][2]));
f.write("%.6f, %.6f, %.6f, %.6f" % (m[0][3], m[1][3], m[2][3], m[3][3]));
f.write("\n");

def saveSAM(self, context, path, startFrame, endFrame):
f = open(path, "w", encoding="utf8", newline="\n");

scene = context.scene;
obj = context.object;
armature = obj.parent;

f.write("Skeleton: skeleton://please edit path to skeleton.ske\n");
f.write("frames: %d\n" % (endFrame - startFrame + 1));
f.write("delay: %.6f\n" % (1.0 / (scene.render.fps / scene.render.fps_base)));
f.write("bones_data:\n");

for bone in armature.pose.bones:
f.write(" " + bone.name + ":\n");
self.writeAnimation(f, scene, startFrame, endFrame, bone);

f.close();
return {'FINISHED'};

def execute(self, context):
path = self.as_keywords()["filepath"];
start = self.as_keywords()["startFrame_prop"];
end = self.as_keywords()["endFrame_prop"];
return self.saveSAM(context, path, start, end);

def menu_func(self, context):
self.layout.operator(ExportSAM.bl_idname, text="Spout Animation Model (.sam)");

def register():
bpy.utils.register_module(__name__);
bpy.types.INFO_MT_file_export.append(menu_func);

def unregister():
bpy.utils.unregister_module(__name__);
bpy.types.INFO_MT_file_export.remove(menu_func);

if __name__ == "__main__":
register()
I highlighted the main part of this exporter. We tried to use "matrix" "matrix_basis" "matrix_channel" with or without recursive multiplication on parents bones ... still stuck :(

I came here to ask for so help cuz i do'nt really have too much time to spend on this and i never used blender before :s
( more than that english is not my first language ^^ )

Thx for responses !

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Mon Jan 21, 2013 11:32 pm

i couldn't figure out this out myself, tried it several times, it's probably easy in the end, but you need to know the right way...

just a hint so far: you can do matrix multiplication just like:

Code: Select all

mat1 * mat2
and you don't need as_keywords() in your case:

Code: Select all

path = self.as_keywords()["filepath"];

# simply access it like
self.filepath
question: how should this look eventually?

Code: Select all

 skeleton://please edit path to skeleton.ske

# output like this?
Skeleton: only_the_filename.ske
I suggest to add 'REGISTER' to the set, so it will be listed in operator log:

Code: Select all

bl_options = {'REGISTER', 'PRESET'}; 
I'm sitting, waiting, wishing, building Blender in superstition...

Alkaelis
Posts: 0
Joined: Mon Jan 21, 2013 6:09 pm

Post by Alkaelis » Tue Jan 22, 2013 6:39 pm

Thx, I didn't know for matrix mult ^^

There is an example of an output: ( matrix are not good ofc )
Skeleton: skeleton://Spout/entities/Spouty/spouty.ske
frames: 31
delay: 0.041667
bones_data:
body:
1: 0.993832, 0.110895, -0.000024, 0.005494, -0.000024, -0.000001, -1.000000, 0.000178, -0.110895, 0.993832, 0.000001, 0.824144, 0.000000, 0.000000, 0.000000, 1.000000
2: 0.994127, 0.108220, -0.000023, 0.005494, -0.000023, -0.000001, -1.000000, 0.000178, -0.108220, 0.994127, 0.000001, 0.824144, 0.000000, 0.000000, 0.000000, 1.000000

CoDEmanX
Posts: 0
Joined: Sun Apr 05, 2009 7:42 pm
Location: Germany

Post by CoDEmanX » Tue Jan 22, 2013 11:51 pm

What parts of this line should reflect blender properties?

skeleton://Spout/entities/Spouty/spouty.ske

- always ".ske" extension (appended)

- for skeletons always skeleton://Spout/entities/ ?

- Spouty = object name?

- spouty = armature name?
I'm sitting, waiting, wishing, building Blender in superstition...

Alkaelis
Posts: 0
Joined: Mon Jan 21, 2013 6:09 pm

Post by Alkaelis » Wed Jan 23, 2013 9:33 am

It's just a path in our directory, nothing else.

My question is only about matricies ...

Post Reply