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.



Here is the link:

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 #####

#

This program is free software; you can redistribute it and/or

modify it under the terms of the GNU General Public License

as published by the Free Software Foundation; either version 2

of the License, or (at your option) any later version.

#

This program is distributed in the hope that it will be useful,

but WITHOUT ANY WARRANTY; without even the implied warranty of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

GNU General Public License for more details.

#

You should have received a copy of the GNU General Public License

along with this program; if not, write to the Free Software Foundation,

Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

#

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

#Author Stephen Jones… S Jones Art.

import bpy

import mathutils

import os

import collections

#you must hand code this

filename=‘C:\c#\Tutorials\08ShadedTextures\Game\Scene\assets\xml\pillar.xml’

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[0], 4), round(v[1], 4), round(v[2], 4)

def roundVec2(v):

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

return round(v[0], 4), round(v[1], 4)

flippy=mathutils.Matrix()

flippy[1].y=0.0

flippy[1].z=-1.0

flippy[2].y=1.0

flippy[2].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[0].use_smooth

quads=list()

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:

quads.append(tmp)

#get data for tris and add to respective Vat

for t in tris:

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

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

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

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[0].data[t.faceIndex].uv1))

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

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

#get data for quads and add to respective Vat

for q in quads:

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

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

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

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

for q in quads:

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[0].data[q.faceIndex].uv1))

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

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

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

#calculate tangents and add to Vat

def cross(u, v):

ret=mathutils.Vector()

ret.x=u[1]*v[2]-u[2]-v[1]

ret.y=u[2]*v[0]-u[0]*v[2]

ret.z=u[0]*v[1]-u[1]*v[0]

return roundVec3(ret)

up=mathutils.Vector()

up.y=1.0

for t in tris:

if t.norm1[0]==up[0] and t.norm1[1]==up[1] and t.norm1[2]==up[2]:

print(“same”)

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

if t.norm2[0]==up[0] and t.norm2[1]==up[1] and t.norm2[2]==up[2]:

print(“same”)

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

if t.norm3[0]==up[0] and t.norm3[1]==up[1] and t.norm3[2]==up[2]:

print(“same”)

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

for q in quads:

if q.norm1[0]==up[0] and q.norm1[1]==up[1] and q.norm1[2]==up[2]:

print(“same”)

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

if q.norm2[0]==up[0] and q.norm2[1]==up[1] and q.norm2[2]==up[2]:

print(“same”)

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

if q.norm3[0]==up[0] and q.norm3[1]==up[1] and q.norm3[2]==up[2]:

print(“same”)

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

if q.norm4[0]==up[0] and q.norm4[1]==up[1] and q.norm4[2]==up[2]:

print(“same”)

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

#multiply vertex positions by 10

def mult(v, n):

ret=mathutils.Vector()

ret.x=v[0]*n

ret.y=v[1]*n

ret.z=v[2]*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)

for q in quads:

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>”

for q in quads:

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