Created
March 4, 2012 21:13
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!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