# Tutorial: Exporting Animation Data from Blender 2.5

Strictly speaking I shouldn’t be uploading this tutorial yet because I have not imported the exported data into a 3d environment. But then the script illustrated is basically a compilation of the bundled export_mdd.py and the export_ply.py scripts, so if they work, then unless I tripped somewhere mine should to. I do know that the script exports what looks like the right sort of numbers, and anyway, it might be useful as a foray into Blender 2.5’s API as it exists as of NOW! Assuming the script is good, there should be little problem reconfiguring the data to output xml in a format able to fool jME3 into believing it is processing an Ogre xml file.

Thank you very much for this tutorial.

I have recently updated the Blender exporter for the 2.59 version. The code can be found at S Jones Art under the c# SDL tab, and is posted below. The tangent exporter part needs explanation because there is a lot of bs spoken about tangent space. Normals are not stored in tangent space they are stored as if the smooth normal of the vertex, for all vertices on the model, was pointing (0,0,1) or up the z axis. The normal map stores offsets, x and y variations off that supposed normal to encode perturbations of the model’s surface. The tangent space is simply a base frame created about the point (vertex to be more accurate, then interpolated) where light hits the model, which base frame is used to rotate the the light vector (etc) so that it shines upon the normal in the normal map as if it would shine upon the normal on the model. All the carry on with texture coordinates is to create a base frame to act as a transformation matrix. But you don’t need all that. Consider the earth as if it had normals all over it. If you use the normal as the z axis of the base frame, and you cross that with true y (0, 1, 0) you get a tangent always parallel to the equator. This tangent is the x axis of the base frame. If you cross the normal and the tangent you get the bitangent which is the y axis of the base frame. This base frame is truly orthogonal, and truly centered about the smoothed normal. Texture space tangents, on the contrary, are either centered about face normals and therefore do not accurately stop light vectors that should fall below the horizon, or they are not orthogonal because the normals need to be bent from face normals into smooth vertex normals while leaving uv coords and therefore tangent and bitangent mapped to the face.

This exporter works by creating lists of faces. Each face is a class which contains 3 (tri) or 4 (quad) sets of attribute data. Copy and past the code into Blender’s text window and hit alt p. You must specify a file path where you want the xml file written… find it after the imports.

[java]# ##### BEGIN GPL LICENSE BLOCK #####

#

#

#

#

# ##### END GPL LICENSE BLOCK #####

#Author Stephen Jones… S Jones Art.

import bpy

import mathutils

import os

import collections

#you must hand code this

frame=1

class Struct:

def init (self, *argv, **argd):

if len(argd):

# Update by dictionary

self.dict.update (argd)

else:

# Update by position

attrs = filter (lambda x: x[0:2] != “__”, dir(self))

for n in range(len(argv)):

setattr(self, attrs[n], argv[n])

class Vat(Struct):

faceIndex=0

v1=v2=v3=v4=0

vert1=vert2=vert3=vert4=mathutils.Vector()

coord1=coord2=coord3=coord4=mathutils.Vector()

norm1=norm2=norm3=norm4=mathutils.Vector()

tan1=tan2=tan3=tan4=mathutils.Vector()

def roundVec3(v):

return round(v, 4), round(v, 4), round(v, 4)

def roundVec2(v):

#uv values are 1.0 - uv to spin things around to fit the geometry

return round(v, 4), round(v, 4)

flippy=mathutils.Matrix()

flippy.y=0.0

flippy.z=-1.0

flippy.y=1.0

flippy.z=0.0

context=bpy.context

scene=context.scene

scene.frame_set(frame)

scene.update

obj=context.object

mesh=obj.to_mesh(scene, True, ‘PREVIEW’)

mesh.transform(obj.matrix_world)

mesh.transform(flippy)

smooth=mesh.faces.use_smooth

tris=list()

count=0

#get face index, add to Vat object and store Vat in either tri or quad list

for f in mesh.faces:

tmp=Vat()

tmp.faceIndex=f.index

if len(f.vertices)=:3:

tris.append(tmp)

if len(f.vertices)==4:

#get data for tris and add to respective Vat

for t in tris:

t.v1=mesh.faces[t.faceIndex].vertices

t.v2=mesh.faces[t.faceIndex].vertices

t.v3=mesh.faces[t.faceIndex].vertices

for t in tris:

t.vert1=roundVec3(tuple(mesh.vertices[t.v1].co))

t.vert2=roundVec3(tuple(mesh.vertices[t.v2].co))

t.vert3=roundVec3(tuple(mesh.vertices[t.v3].co))

if mesh.faces[q.faceIndex].use_smooth:

t.norm1=roundVec3(tuple(mesh.vertices[t.v1].normal))

t.norm2=roundVec3(tuple(mesh.vertices[t.v2].normal))

t.norm3=roundVec3(tuple(mesh.vertices[t.v3].normal))

else:

t.norm1=roundVec3(tuple(mesh.faces[t.faceIndex].normal))

t.norm2=roundVec3(tuple(mesh.faces[t.faceIndex].normal))

t.norm3=roundVec3(tuple(mesh.faces[t.faceIndex].normal))

t.coords1=roundVec2(tuple(mesh.uv_textures.data[t.faceIndex].uv1))

t.coords2=roundVec2(tuple(mesh.uv_textures.data[t.faceIndex].uv2))

t.coords3=roundVec2(tuple(mesh.uv_textures.data[t.faceIndex].uv3))

q.v1=mesh.faces[q.faceIndex].vertices

q.v2=mesh.faces[q.faceIndex].vertices

q.v3=mesh.faces[q.faceIndex].vertices

q.v4=mesh.faces[q.faceIndex].vertices

q.vert1=roundVec3(tuple(mesh.vertices[q.v1].co))

q.vert2=roundVec3(tuple(mesh.vertices[q.v2].co))

q.vert3=roundVec3(tuple(mesh.vertices[q.v3].co))

q.vert4=roundVec3(tuple(mesh.vertices[q.v4].co))

if mesh.faces[q.faceIndex].use_smooth:

q.norm1=roundVec3(tuple(mesh.vertices[q.v1].normal))

q.norm2=roundVec3(tuple(mesh.vertices[q.v2].normal))

q.norm3=roundVec3(tuple(mesh.vertices[q.v3].normal))

q.norm4=roundVec3(tuple(mesh.vertices[q.v4].normal))

else:

q.norm1=roundVec3(tuple(mesh.faces[q.faceIndex].normal))

q.norm2=roundVec3(tuple(mesh.faces[q.faceIndex].normal))

q.norm3=roundVec3(tuple(mesh.faces[q.faceIndex].normal))

q.norm4=roundVec3(tuple(mesh.faces[q.faceIndex].normal))

q.coords1=roundVec2(tuple(mesh.uv_textures.data[q.faceIndex].uv1))

q.coords2=roundVec2(tuple(mesh.uv_textures.data[q.faceIndex].uv2))

q.coords3=roundVec2(tuple(mesh.uv_textures.data[q.faceIndex].uv3))

q.coords4=roundVec2(tuple(mesh.uv_textures.data[q.faceIndex].uv4))

#calculate tangents and add to Vat

def cross(u, v):

ret=mathutils.Vector()

ret.x=u*v-u-v

ret.y=u*v-u*v

ret.z=u*v-u*v

return roundVec3(ret)

up=mathutils.Vector()

up.y=1.0

for t in tris:

if t.norm1==up and t.norm1==up and t.norm1==up:

print(“same”)

t.tan1=cross(up, t.norm1)

if t.norm2==up and t.norm2==up and t.norm2==up:

print(“same”)

t.tan2=cross(up, t.norm2)

if t.norm3==up and t.norm3==up and t.norm3==up:

print(“same”)

t.tan3=cross(up, t.norm3)

if q.norm1==up and q.norm1==up and q.norm1==up:

print(“same”)

q.tan1=cross(up, q.norm1)

if q.norm2==up and q.norm2==up and q.norm2==up:

print(“same”)

q.tan2=cross(up, q.norm2)

if q.norm3==up and q.norm3==up and q.norm3==up:

print(“same”)

q.tan3=cross(up, q.norm3)

if q.norm4==up and q.norm4==up and q.norm4==up:

print(“same”)

q.tan4=cross(up, q.norm4)

#multiply vertex positions by 10

def mult(v, n):

ret=mathutils.Vector()

ret.x=v*n

ret.y=v*n

ret.z=v*n

return roundVec3(ret)

for t in tris:

t.vert1=mult(t.vert1, 10)

t.vert2=mult(t.vert2, 10)

t.vert3=mult(t.vert3, 10)

q.vert1=mult(q.vert1, 10)

q.vert2=mult(q.vert2, 10)

q.vert3=mult(q.vert3, 10)

q.vert4=mult(q.vert4, 10)

#construct xml print strings

tv="<tv>"

tn="<tn>"

tc="<tc>"

tt="<tt>"

for t in tris:

tv=tv + "%.6f %.6f %.6f " %t.vert1

tv=tv + "%.6f %.6f %.6f " %t.vert2

tv=tv + "%.6f %.6f %.6f " %t.vert3

tn=tn + "%.6f %.6f %.6f " %t.norm1

tn=tn + “%.6f %.6f %.6f " %t.norm2

tn=tn + “%.6f %.6f %.6f " %t.norm3

tc=tc + “%.6f %.6f " %t.coords1

tc=tc + “%.6f %.6f " %t.coords2

tc=tc + “%.6f %.6f " %t.coords3

tt=tt + “%.6f %.6f %.6f " %t.tan1

tt=tt + “%.6f %.6f %.6f " %t.tan2

tt=tt + “%.6f %.6f %.6f " %t.tan3

tv=tv.rstrip(’ ‘)

tv=tv + “</tv>n”

tn=tn.rstrip(’ ‘)

tn=tn + “</tn>n”

tc=tc.rstrip(’ ‘)

tc=tc + “</tc>n”

tt=tt.rstrip(’ ')

tt=tt + “</tt>n”

qv=”<qv>”

qn=”<qn>”

qc=”<qc>”

qt=”<qt>”

qv=qv + "%.6f %.6f %.6f " %q.vert1

qv=qv + "%.6f %.6f %.6f " %q.vert2

qv=qv + "%.6f %.6f %.6f " %q.vert3

qv=qv + "%.6f %.6f %.6f " %q.vert4

qn=qn + "%.6f %.6f %.6f " %q.norm1

qn=qn + "%.6f %.6f %.6f " %q.norm2

qn=qn + "%.6f %.6f %.6f " %q.norm3

qn=qn + "%.6f %.6f %.6f " %q.norm4

qc=qc + "%.6f %.6f " %q.coords1

qc=qc + "%.6f %.6f " %q.coords2

qc=qc + "%.6f %.6f " %q.coords3

qc=qc + "%.6f %.6f " %q.coords4

qt=qt + "%.6f %.6f %.6f " %q.tan1

qt=qt + "%.6f %.6f %.6f " %q.tan2

qt=qt + “%.6f %.6f %.6f " %q.tan3

qt=qt + “%.6f %.6f %.6f " %q.tan4

qv=qv.rstrip(’ ‘)

qv=qv + “</qv>n”

qn=qn.rstrip(’ ‘)

qn=qn + “</qn>n”

qc=qc.rstrip(’ ‘)

qc=qc + “</qc>n”

qt=qt.rstrip(’ ‘)

qt=qt + “</qt>n”

file=open(filename, ‘w’)

file.write(’<?xml version=“1.0” encoding=“utf-8”?>n’)

file.write(’<staticModel>n’)

file.write(tv)

file.write(tn)

file.write(tc)

file.write(tt)

file.write(qv)

file.write(qn)

file.write(qc)

file.write(qt)

file.write(’<mat> </mat>n’)

file.write(’<base>back.png</base>n’)

file.write(’<norm s=“1” t=“1”>subwayHKwall_n.png</norm>n’)

file.write(’<tile s=“1” t=“1”>subwayHKpillar_t.png</tile>n’)

file.write(”</staticModel>”)

file.close()[/java]

1 Like