Skip to content

Instantly share code, notes, and snippets.

@danielchasehooper
Created March 4, 2012 21:13
Show Gist options
  • Save danielchasehooper/1974808 to your computer and use it in GitHub Desktop.
Save danielchasehooper/1974808 to your computer and use it in GitHub Desktop.
This is the Blender export script used to export Percepto's animated models
#!BPY
# Blender animation export script by Daniel Hooper
# www.danielhooper.tumblr.com
import bpy
from os.path import basename
import struct
GL_FLOAT = 5126
GL_UNSIGNED_INT = 5125
def writeAttribHeader(file, numElements, elementType, elementWidth):
# byteSize, dataType, vectorWidth, numelements
file.write(struct.pack('IIII',numElements*4*elementWidth, elementType, elementWidth, numElements))
def writeTOC(mesh, out):
numVerts = len(mesh.faces)*3
size = numVerts*4
positionOffset = 16
texCoordOffset = positionOffset + 16 + numVerts*3*4
out.write(struct.pack('IIII', positionOffset, texCoordOffset, 0 , 0))
def write(object, filename, elementData=False):
if len(object.data.uv_textures) == 0:
print("No texCords")
return
# exit edit mode, otherwise you can't access data, which is stupid.
if object.mode == 'EDIT':
bpy.ops.object.mode_set(mode='OBJECT')
ob = triangulatedObject(object)
mesh = ob.data
out = open("%s/%s.mdl" % (filename,object.name), "wb")
#table of contents
writeTOC(mesh, out)
numVerts = len(mesh.faces)*3
# vertices
writeAttribHeader(out, numVerts, GL_FLOAT, 3)
for face in mesh.faces:
for vertIndexInFace, vertIndex in enumerate(reversed(face.vertices)):
vpos = mesh.vertices[vertIndex].co
out.write(struct.pack('fff',vpos.x, vpos.z, vpos.y))
# uv
writeAttribHeader(out, numVerts, GL_FLOAT, 2)
exportUVs(mesh,out)
out.close()
bpy.context.scene.objects.unlink(ob)
bpy.data.objects.remove(ob)
def getUV(mesh, face_index, vert_index):
return mesh.uv_textures.active.data[face_index].uv[vert_index]
def exportUVs(mesh, out):
for faceIndex, face in enumerate(mesh.faces):
u = mesh.uv_textures[0].data[faceIndex]
uvs = u.uv3,u.uv2,u.uv1
for vertIndexInFace, vertIndex in enumerate(reversed(face.vertices)):
out.write(struct.pack('ff',uvs[vertIndexInFace].x, 1-uvs[vertIndexInFace].y))
def exportVertices(mesh, out, normals=False):
if normals:
for vertex in mesh.vertices:
out.write(struct.pack('fff',vertex.co.x, vertex.co.z, vertex.co.y))
out.write(struct.pack('fff',vertex.normal.x, vertex.normal.y, vertex.normal.z))
else:
for vertex in mesh.vertices:
out.write(struct.pack('fff',vertex.co.x, vertex.co.z, vertex.co.y))
def normals(mesh, out):
for face in mesh.faces:
for vertIndexInFace, vertIndex in enumerate(reversed(face.vertices)):
if face.use_smooth:
norm = mesh.vertices[verIndex].normal
else:
norm = face.normal
out.write(struct.pack('fff',norm.x, norm.y, norm.z))
def elementDataForMesh(mesh):
helperVertTable = []
vertexTable = []
elementTable = []
for faceIndex, face in enumerate(mesh.faces):
u = mesh.uv_textures[0].data[faceIndex]
uvs = u.uv3,u.uv2,u.uv1
for vertexIndexInFace, vertexIndex in enumerate(reversed(face.vertices)):
pos = mesh.vertices[vertexIndex].co
vertData = [pos.x,pos.y,pos.z,uvs[vertexIndexInFace].x, uvs[vertexIndexInFace].y]
try:
tableIndex = helperVertTable.index(vertData)
except ValueError:
helperVertTable.append(vertData)
tableIndex = len(helperVertTable) - 1
vertexTable.append({'vertexIndex':vertexIndex,'faceIndex':faceIndex,'vertIndexInFace':vertexIndexInFace})
elementTable.append(tableIndex)
return (vertexTable, elementTable)
def triangulatedObject(object):
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.select_name(name=object.name)
bpy.ops.object.duplicate()
newObject = bpy.context.object
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.object.mode_set(mode="OBJECT")
return newObject
def actionsForObject(obj):
return bpy.data.actions
def exportAnimatedMesh(object, path):
#create a copy to convert to Tris
newObject = triangulatedObject(object)
out = open("%s/%s.amdl" % (path, object.name), "wb")
#create a mesh with modifiers applied so we know how many faces/verts there are
mesh = newObject.to_mesh(bpy.context.scene,True,'RENDER')
vertTable, elementTable = elementDataForMesh(mesh);
#get animation markers
markers = sorted(bpy.context.scene.timeline_markers, key=lambda mark: mark.frame)
start = markers[0].frame
end = markers[-1].frame
frameOffset = -markers[0].frame;
print("start:",start)
print("end:",end)
#write TOC
TOCHeaderSize = 16
attribHeaderSize = 16
numberOfFrames = end-start
elementOffset = TOCHeaderSize
elementCount = len(elementTable)
elementSize = elementCount*4
uvOffset = elementOffset + attribHeaderSize + elementSize
uvCount = len(vertTable)
uvSize = uvCount * 2 * 4
animationDataOffset = uvOffset + attribHeaderSize + uvSize
animationDataCount = len(markers)-1
animationDataSize = animationDataCount * 12
positionOffset = animationDataOffset + 4 + animationDataSize
positionCount = uvCount
positionSize = positionCount * 3 * 4
AnimatedPositionCount = positionCount*numberOfFrames
out.write(struct.pack('IIII', positionOffset, uvOffset, elementOffset, animationDataOffset))
#print("Raw vert count", len(mesh.vertices),"Vertex count:", len(vertTable))
#element table
writeAttribHeader(out, elementCount, GL_UNSIGNED_INT, 1)
for element in elementTable:
out.write(struct.pack("I",element))
#Write UVs
writeAttribHeader(out, uvCount, GL_FLOAT, 2)
for vInfo in vertTable:
u = mesh.uv_textures[0].data[vInfo['faceIndex']]
uv = [u.uv3,u.uv2,u.uv1][vInfo['vertIndexInFace']]
out.write(struct.pack("ff",uv.x,(1-uv.y)))
bpy.data.meshes.remove(mesh)
#write animation data
out.write(struct.pack("I", animationDataCount)); # write number of animations
for i in range(0,animationDataCount):
animationStart = markers[i].frame + frameOffset
animationEnd = markers[i+1].frame + frameOffset
interpRate = float(markers[i].name)
out.write(struct.pack("IIf", animationStart, animationEnd, interpRate))
#Vertex attrib header
writeAttribHeader(out, AnimatedPositionCount, GL_FLOAT, 3)
#Vertices 3:14:15 9/26/54
for frame in range(start,end+1):
print("Exporting frame", frame)
bpy.context.scene.frame_set(frame)
m = newObject.to_mesh(bpy.context.scene,True,'RENDER')
for vInfo in vertTable:
vertexPos = m.vertices[vInfo['vertexIndex']].co
out.write(struct.pack("fff",vertexPos.x,vertexPos.z,vertexPos.y))
bpy.data.meshes.remove(m)
#clean up
out.close()
bpy.context.scene.objects.unlink(newObject)
bpy.data.objects.remove(newObject)
bpy.ops.object.select_name(name=object.name)
path = "/Users/Daniel/Code/iOS/Percepto/GameResources/Models"
exportAnimatedMesh(bpy.context.object, path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment