#!BPY """ Name: 'Nintendo DS Display Lists' Blender: 243 Group: 'Export' Tooltip: 'Export .c and .h files for use with Nintendo DS' """ __author__ = "Brian \"Sir Alaran\" Schott and David \"Grizzly Adams\" Kuder" __url__ = ("http://grizzly.thewaffleiron.net/blender") __bpydoc__ = """\ This script exports Nintendo DS Display Lists in .c files from Blender. """ # Last updated June 8 2008 #~ This program is free software; you can redistribute it and/or #~ modify it under the terms of the GNU General Public License version #~ 2 as published by the Free Software Foundation. #~ 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., 59 Temple Place - Suite 330, Boston, #~ MA 02111-1307, USA. import Blender from Blender import Draw, BGL, Scene, Mesh, Window, sys, Registry import BPyMessages import bpy import os ExportCredit = "\n/* Generated by DS_export_many.py */\n\n" EVENT_NOEVENT = 1 EVENT_MESH = 2 EVENT_SCENE = 3 EVENT_WORLD = 4 EVENT_TOGTEX = 5 EVENT_TOGCOL = 6 EVENT_TOGNOR = 7 EVENT_SCALETEX = 8 EVENT_SCALENOR = 9 EVENT_SCALEMESH = 10 EVENT_FILETEXT = 11 EVENT_FILEPICK = 12 EVENT_AUTOEXIT = 13 EVENT_EXIT = 14 topFileName = "" AutoExit = 0 ExportTextures = 1 ExportNormals = 1 ExportColors = 1 # Texture coordinates are 3.12 fixed point (16bit signed) textureScale = 1.0 # Normals are 0.9 fixed point (10bit signed) normalScale = 510.0/512.0 # Verts are 3.12 fixed point (16bit signed) meshScale = 1.0 textMessage = "" def save_Registry(): global AutoExit global textureScale, normalScale, meshScale global ExportTextures, ExportNormals, ExportColors d = {} d['AutoExit'] = AutoExit d['textureScale'] = textureScale d['normalScale'] = normalScale d['meshScale'] = meshScale d['ExportTextures'] = ExportTextures d['ExportNormals'] = ExportNormals d['ExportColors'] = ExportColors Blender.Registry.SetKey('NDSDisplayList', d, True) def load_Registry(): global AutoExit global textureScale, normalScale, meshScale global ExportTextures, ExportNormals, ExportColors rdict = Registry.GetKey('NDSDisplayList', True) if rdict: try: textureScale = rdict['textureScale'] normalScale = rdict['normalScale'] meshScale = rdict['meshScale'] ExportTextures = rdict['ExportTextures'] ExportNormals = rdict['ExportNormals'] ExportColors = rdict['ExportColors'] AutoExit = rdict['AutoExit'] except: save_Registry() class PackCommand: command = "FIFO_NOP" data = "" size = 0 def __init__(self): pass def saveDisplayList(name, mesh, fileName): global textureScale,normalScale,meshScale global ExportTextures,ExportColors,ExportNormals global textMessage file = open(fileName, "w") file.write("#include \n" + ExportCredit) pack = [] facetype = 0 facecount = len(mesh.faces) * 1.0 facedone = 0.0 for face in mesh.faces: Window.DrawProgressBar(facedone / facecount, "Exporting Faces...") facedone = facedone + 1.0 # Save memory & commands by using the correct face mode # instead of converting quads to tris. facesize = len(face.verts) if facesize != facetype: if facesize == 4: facetype = facesize pcmd = PackCommand() pcmd.command = "FIFO_BEGIN" pcmd.data = "GL_QUADS," pcmd.size = 1 pack.append(pcmd) else: facetype = facesize pcmd = PackCommand() pcmd.command = "FIFO_BEGIN" pcmd.data = "GL_TRIANGLES," pcmd.size = 1 pack.append(pcmd) # We only write the normal once per face, reset the flag # indicating we haven't sent one this face. wrotenormal = 0 wrotecolors = 0 # Write out all verts in face for i in range(facesize): # Export Texture Coordinates if (ExportTextures == 1) and (mesh.faceUV == True): pcmd = PackCommand() pcmd.command = "FIFO_TEX_COORD" pcmd.data = "TEXTURE_PACK(floattov16(" + str(round(face.uv[i][0]*textureScale*4096.0)/4096.0) + "), floattov16(" + str(round(face.uv[i][1]*textureScale*4096.0)/4096.0) + "))," pcmd.size = 1 pack.append(pcmd) # Export Face Normals if (ExportNormals == 1) and (wrotenormal == 0): pcmd = PackCommand() pcmd.command = "FIFO_NORMAL" pcmd.data = "NORMAL_PACK(floattov10(" + str(round(face.no[0]*normalScale,3)) +"), floattov10(" + str(round(face.no[1]*normalScale,3)) + "), floattov10(" + str(round(face.no[2]*normalScale,3)) + "))," pcmd.size = 1 pack.append(pcmd) wrotenormal = 1 # Export vertex colors if (ExportColors == 1) and (mesh.vertexColors == True): pcmd = PackCommand() pcmd.command = "FIFO_COLOR" pcmd.data = "RGB15(" + str(int(round(face.col[i].r/8))) + ", " + str(int(round(face.col[i].g/8))) + ", " + str(int(round(face.col[i].b/8))) + ")," pcmd.size = 1 pack.append(pcmd) # or Export Default color elif (ExportColors == 1) and (wrotecolors == 0): wrotecolors = 1 pcmd = PackCommand() pcmd.command = "FIFO_COLOR" pcmd.data = "RGB15(31,31,31)," pcmd.size = 1 pack.append(pcmd) pcmd = PackCommand() pcmd.command = "FIFO_VERTEX16" pcmd.data = "VERTEX_PACK(floattov16(" + str(round(face.verts[i].co[0]*meshScale*4096.0)/4096.0) + "), floattov16(" + str(round(face.verts[i].co[1]*meshScale*4096.0)/4096.0) + ")), VERTEX_PACK(floattov16(" + str(round(face.verts[i].co[2]*meshScale*4096.0)/4096.0) + "), 0)," pcmd.size = 2 pack.append(pcmd) # Not needed on NDS #pcmd = PackCommand() #pcmd.command = "FIFO_END" #pack.append(pcmd) # Add FIFO_NOP commands to fill up the buffer packleft = int((((len(pack)+3)/4)*4)-len(pack)) if packleft > 0: print 'NDS DisplayList %s padded with %i NOP commands.' % (name, packleft) for i in range(packleft): pcmd = PackCommand() pack.append(pcmd) packsize = 0 for pcmd in pack: packsize = packsize + pcmd.size packsize = packsize + (len(pack)/4) print 'NDS DisplayList %s contains %i commands, and is %i entries long.' % (name, len(pack), packsize) file.write("u32 " + name + " [" + str(packsize+1) + "] =\n{\n") file.write("\t" + str(packsize) + ",\n") # Write out packed commands, 4 at a time. while len(pack) >= 4: file.write("\tFIFO_COMMAND_PACK(" + pack[0].command + ", " + pack[1].command + ", " + pack[2].command + ", " + pack[3].command + "),\n") # only output data if theres data to output. if len(pack[0].data): file.write("\t" + pack[0].data + "\n") if len(pack[1].data): file.write("\t" + pack[1].data + "\n") if len(pack[2].data): file.write("\t" + pack[2].data + "\n") if len(pack[3].data): file.write("\t" + pack[3].data + "\n") pack.pop(0) pack.pop(0) pack.pop(0) pack.pop(0) file.write("};\n") # this should never happen if len(pack) > 0: textMessage = textMessage + "WARNING: Possible underflow in DS_export.py : " + str(len(pack)) + " commands left.\n" file.write("#warn Possible underflow in DS_export.py : " + str(len(pack)) + " commands left.\n") print 'WARNING: Possible underflow in DS_export.py : %i commands left.' % len(pack) file.close() Window.DrawProgressBar(1.0, "Finished") def export_mesh(mesh, fileName): global textMessage t = sys.time() # Saves the editmode state and go's out of # editmode if its enabled, we cant make # changes to the mesh data while in editmode. is_editmode = Window.EditMode() if is_editmode: Window.EditMode(0) # Change cursor to indicate we are busy Window.WaitCursor(1) # Export a header for the mesh meshPath = sys.makename(fileName) meshHdr = open(meshPath + ".h", "w") meshHdr.write("#include \n" + ExportCredit) meshHdr.write("extern u32 " + mesh.name + "[];\n") meshHdr.close() meshName = sys.basename(meshPath) # Run the mesh exporting function saveDisplayList(meshName, mesh, meshPath + ".c") # Restore editmode if it was enabled if is_editmode: Window.EditMode(1) # Timing the script is a good way to be aware on any speed hits when scripting textMessage = "DisplayList " + meshName + " Exported in " + str((sys.time()-t)) + " seconds\n" print 'NDS DisplayList %s Exported in %.2f seconds' % (meshName, (sys.time()-t)) Window.WaitCursor(0) def export_scene(sce, fileName): t = sys.time() scenePath = sys.makename(fileName) sceneHdr = open(scenePath + ".h", "w") sceneHdr.write("#include \n" + ExportCredit) # Grab objects from the scene for ob in sce.objects: if ob.type == 'Mesh': mesh = ob.getData(mesh=1) # old NMesh api is default if os.path.isdir(scenePath) != True: os.mkdir(scenePath) sceneHdr.write("#include \"" + sce.name + "/" + mesh.name + ".h\"\n") meshPath = sys.join(scenePath, mesh.name) export_mesh(mesh, meshPath) sceneHdr.close() # Timing the script is a good way to be aware on any speed hits when scripting textMessage = "Scene " + sce.name + " Exported in " + str((sys.time()-t)) + " seconds\n" print 'NDS DisplayList %s Exported in %.2f seconds' % (sce.name, (sys.time()-t)) def export_world(fileName): t = sys.time() worldPath = sys.makename(fileName) worldHdr = open(worldPath + ".h", "w") worldHdr.write("#include \n" + ExportCredit) # Gets the current scenes, there can be many scenes in 1 blend file. for sce in bpy.data.scenes: if os.path.isdir(worldPath) != True: os.mkdir(worldPath) worldHdr.write("#include \"" + sys.basename(worldPath) + "/" + sce.name + ".h\"\n") scenePath = sys.join(worldPath, sce.name + ".h") export_scene(sce, scenePath); worldHdr.close() # Timing the script is a good way to be aware on any speed hits when scripting textMessage = "All Scenes Exported in " + str((sys.time()-t)) + " seconds\n" print 'All Scenes Exported in %.2f seconds' % (sys.time()-t) def draw(): # Event ID references global EVENT_NOEVENT,EVENT_TOGTEX,EVENT_TOGCOL,EVENT_TOGNOR global EVENT_SCALETEX,EVENT_SCALENOR,EVENT_SCALEMESH global EVENT_AUTOEXIT,EVENT_FILEPICK, EVENT_FILETEXT global EVENT_MESH,EVENT_SCENE,EVENT_WORLD,EVENT_EXIT # Variable references global ExportTextures,ExportColors,ExportNormals global textureScale,normalScale,meshScale global textMessage,AutoExit # Draw object references global winTitleText,winOptionsText,winExportText global winTextureScale,winNormalScale,winMeshScale global winTextureToggle,winColorsToggle,winNormalsToggle global winWorldButton,winSceneButton,winMeshButton global winFileString,winFileButton,winExitButton global winMessageText,winAutoExitToggle BGL.glColor3f(0,0,0) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) # Title BGL.glRasterPos2d(8, 160) winTitleText = Draw.Text("Nintendo DS DisplayList Exporter") # Status/Error Messages BGL.glRasterPos2d(8, 35) winMessageText = Draw.Text(textMessage) # Option Label BGL.glRasterPos2d(8, 130) winOptionsText = Draw.Text("Options:") # Option Toggles winTextureToggle = Draw.Toggle("Texture Coordinates", EVENT_TOGTEX, 10, 105, 220, 18, ExportTextures) winNormalsToggle = Draw.Toggle("Normals", EVENT_TOGNOR, 10, 80, 220, 18, ExportNormals) winColorsToggle = Draw.Toggle("Colors", EVENT_TOGCOL, 10, 55, 220, 18, ExportColors) # Scaling Label BGL.glRasterPos2d(238, 130) winScaleText = Draw.Text("Scaling:") # Scaling Controls winTextureScale = Draw.Number("Texture:", EVENT_SCALETEX, 240, 105, 220, 18, textureScale, 1.0/4096.0, 32767.0/4096.0) winNormalScale = Draw.Number("Normal:", EVENT_SCALENOR, 240, 80, 220, 18, normalScale, 1.0/512.0, 511.0/512.0) winMeshScale = Draw.Number("Mesh:", EVENT_SCALEMESH, 240, 55, 220, 18, meshScale, 1.0/4096.0, 32767.0/4096.0) # Action Label BGL.glRasterPos2d(468, 130) winExportText = Draw.Text("Export:") # Action Buttons winWorldButton = Draw.Button("All Scenes", EVENT_WORLD, 470, 105, 220, 18) winSceneButton = Draw.Button("Active Scene", EVENT_SCENE, 470, 80, 220, 18) winMeshButton = Draw.Button("Active Mesh", EVENT_MESH, 470, 55, 220, 18) # File Selector winFileString = Draw.String("FileName:", EVENT_FILETEXT, 10, 10, 510, 18, topFileName, 320, "File Name", newFileText) winFileButton = Draw.Button("...", EVENT_FILEPICK, 520, 10, 18, 18) # AutoExit Toggle winAutoExitToggle = Draw.Toggle("Auto Exit", EVENT_AUTOEXIT, 550, 10, 65, 18, AutoExit) # Exit Button winExitButton = Draw.Button("Exit", EVENT_EXIT, 625, 10, 65, 18) def event(evt, val): if (evt == Draw.QKEY and not val): save_Registry() Draw.Exit() def newFileText(evt, val): global topFileName topFileName = val Draw.Redraw(1) def newFilePick(fileName): global topFileName topFileName = fileName Draw.Redraw(1) def bevent(evt): global topFileName,textMessage global EVENT_NOEVENT,EVENT_TOGTEX,EVENT_TOGCOL,EVENT_TOGNOR global EVENT_SCALETEX,EVENT_SCALENOR,EVENT_SCALEMESH global EVENT_AUTOEXIT,EVENT_FILEPICK, EVENT_FILETEXT global EVENT_MESH,EVENT_SCENE,EVENT_WORLD,EVENT_EXIT global ExportTextures,ExportColors,ExportNormals global textureScale,normalScale,meshScale global winTextureScale,winNormalScale,winMeshScale global AutoExit if (evt == EVENT_EXIT): save_Registry() Draw.Exit() elif (evt == EVENT_AUTOEXIT): AutoExit = 1 - AutoExit save_Registry() elif (evt == EVENT_TOGTEX): ExportTextures = 1 - ExportTextures save_Registry() elif (evt == EVENT_TOGCOL): ExportColors = 1 - ExportColors save_Registry() elif (evt == EVENT_TOGNOR): ExportNormals = 1 - ExportNormals save_Registry() elif (evt == EVENT_SCALETEX): textureScale = winTextureScale.val save_Registry() elif (evt == EVENT_SCALENOR): normalScale = winNormalScale.val save_Registry() elif (evt == EVENT_SCALEMESH): meshScale = winMeshScale.val save_Registry() elif (evt == EVENT_MESH): textMessage = "" sce = bpy.data.scenes.active ob_act = sce.objects.active if not ob_act or ob_act.type != 'Mesh': BPyMessages.Error_NoMeshActive() else: mesh = ob_act.getData(mesh=1) export_mesh(mesh, topFileName) if AutoExit: Draw.Exit() elif (evt == EVENT_SCENE): textMessage = "" sce = bpy.data.scenes.active export_scene(sce, topFileName) if AutoExit: Draw.Exit() elif (evt == EVENT_WORLD): textMessage = "" export_world(topFileName) if AutoExit: Draw.Exit() elif (evt == EVENT_FILEPICK): Window.FileSelector(newFilePick, "Export Basename:") Draw.Redraw(1) def main(fileName): global topFileName topFileName = fileName load_Registry() Draw.Register(draw, event, bevent) # This lets you can import the script without running it if __name__ == '__main__': Blender.Window.FileSelector(main, "Save File")