racer_theme_export.py

Go to the documentation of this file.
00001 #!BPY
00002 # This Python file uses the following encoding: utf-8
00003 
00004 """
00005 Name: 'Racer theme Exporter'
00006 Blender: 248
00007 Group: 'Export'
00008 Tooltip: 'Export meshes to a racer theme'
00009 """
00010 
00011 ##@file python/racer_theme_export.py
00012 #@brief Provide a Blender exporter for track themes.
00013 #@author James Legg
00014 
00015 # Copyright © 2009, 2010 James Legg.
00016 # This program is free software: you can redistribute it and/or modify
00017 # it under the terms of the GNU General Public License as published by
00018 # the Free Software Foundation, either version 3 of the License, or
00019 # (at your option) any later version.
00020 
00021 ## @package racer_theme_export
00022 #  Export meshes in a scene to a racer theme.
00023 #
00024 # There are four blender meshes required for each segment. They each need to
00025 # have the same base name in order to be linked, and a tail to specify which
00026 # of the 4 meshes they are.
00027 # - A mesh object name ending in _g is used for graphics.
00028 # - A mesh object name ending in _a is used for AI.
00029 # - A mesh object name ending in _w is used for wall collisions.
00030 # - A mesh object name ending in _f is used for floor physics, so it can be
00031 #   driven on.
00032 #
00033 # The points where edges can be connected are given by the positions of
00034 # curves objects with a name ending in _0 to _9. The same prefix as the other
00035 # meshes must be used to link them.
00036 # The curve data should be shared and used for each different cross section.
00037 # The positions and angles of the curve objects relative to the graphics mesh
00038 # object is used to find where the connections should be.
00039 #
00040 # The positive y direction is treated as forwards, the positive x direction is
00041 # sideways across the track to the right, and the positive z direction is
00042 # vertically up from the track. This is important when you want to use a mesh
00043 # along an edge.
00044 #
00045 # The objects used for an edge will be bent into a curve to fit the edge's
00046 # path. You must not place faces in such a way that holes appear when this
00047 # happens. A normal corner will be OK when the mesh is well subdivided along
00048 # the y direction, but the road surface should also be divided in the x
00049 # direction if the road twists around the y axis.
00050 #
00051 # The physics floor mesh can be more subdivided than the graphical mesh to
00052 # reduce wobbly / slow patches in twisted road.
00053 
00054 import Blender
00055 import bpy
00056 
00057 ## Write the theme in the current scene to a file.
00058 # @param filename The filename to write to.
00059 def write(filename):
00060     print "Opening file..."
00061     out = file(filename, "w")
00062     #sce= bpy.data.scenes.active
00063     print "Finding data..."
00064     # check for a text containg the path.
00065     found = False
00066     for text in Blender.Text.Get() :
00067         if text.getName() == "Texture path":
00068                 texture_path = text.asLines()[0]
00069                 found = True
00070     if not found:
00071         # no path, guess from the filename.
00072         texture_path = 'data/' + filename.rpartition('/')[2]
00073         texture_path_text = Blender.Text.New("Texture path")
00074         texture_path_text.write(texture_path)
00075         Blender.Draw.PupMenu("Info|I'm guessing the texture path is '" + texture_path + "'. If this is incorrect, change the path in the text buffer called 'Texture path' and export again.")
00076     export = {}
00077     piece_names = [];
00078     mesh_codes_b = ['_g', '_a', '_w', '_f']
00079     mesh_codes = set(mesh_codes_b)
00080     for ob in Blender.Scene.GetCurrent().objects:
00081         if ob.getType() == 'Mesh' or ob.getType() == 'Curve':
00082             name = ob.name
00083             #print "    Adding " + name
00084             export[name] = ob
00085             for code in mesh_codes:
00086                 if name[len(name)-2:] == code:
00087                     #print "        code " + code + ", base " + name[:len(name)-2]
00088                     piece_names[:0] = [name[:len(name)-2]]
00089     #only output each piece once.
00090     piece_names = set(piece_names);
00091     print "Names to export: "
00092     print piece_names
00093     # check valid
00094     print "Checking data..."
00095     for piece in piece_names:
00096         for name in [piece + x for x in mesh_codes]:
00097             #print "    Trying to find " + name
00098             if not name in export:
00099                 #moan and return, doesn't exist
00100                 print "    Cannot find " + name + ", aborting."
00101                 Blender.Draw.PupMenu("Error|Expected a mesh object called " + name + ", but could not find it in this scene.")
00102                 return;
00103     #valid
00104     print "Writing data..."
00105     # scheme stuff
00106     # skybox data
00107     out.write ("6 ")
00108     out.write("%s %s %s %s %s %s " % (format_string(texture_path + "/sky.png.+z"),
00109                                       format_string(texture_path + "/sky.png.-z"),
00110                                       format_string(texture_path + "/sky.png.-x"),
00111                                       format_string(texture_path + "/sky.png.+x"),
00112                                       format_string(texture_path + "/sky.png.-y"),
00113                                       format_string(texture_path + "/sky.png.+y")))
00114     out.write('%i ' % (len(piece_names)))
00115     curve_data_names = {}
00116     for piece in piece_names:
00117         # texture placeholder
00118         out.write("%s " % format_string(texture_path + '/' + piece + ".png"))
00119         #each required mesh. (graphics / ai / walls / floor).
00120         for name in [piece + x for x in mesh_codes_b]:
00121             #print "    Writing " + name
00122             write_mesh(out, export[name])
00123         #human readable name
00124         print piece
00125         out.write("%s " % format_string(piece))
00126         # Output the SegmentConnections. First find some:
00127         # They will be curves with the same base name.
00128         connections = {}
00129         for name in [piece + '_' + x for x in "1234567890"]:
00130             if name in export:
00131                 connections[name] = export[name]
00132         # the number of connections.
00133         out.write('%i ' % len(connections.keys()))
00134         # location offset due to graphics mesh.
00135         location_offset = export[piece + '_g'].getLocation()
00136         for con_name in connections:
00137             connection = connections[con_name]
00138             # The cross_Section_id. This is an id unique to all curve data names.
00139             curve_data_name = connection.data.getName()
00140             if not curve_data_name in curve_data_names:
00141                 curve_data_names[curve_data_name] = len(curve_data_names.keys())
00142             out.write('%i ' % curve_data_names[curve_data_name])
00143             # The position. This is offset by the graphics meshes origin.
00144             position = connection.getLocation('worldspace')
00145             out.write('%r %r %r ' % (position[0] - location_offset[0], position[1] - location_offset[1], position[2] - location_offset[2]))
00146             # Write rotation as a quaternion
00147             euler = connection.getEuler('worldspace').copy()
00148             # Convert to degrees first
00149             euler.x *= 57.295779513
00150             euler.y *= 57.295779513
00151             euler.z *= 57.295779513
00152             quaternion = euler.toQuat()
00153             out.write('%r %r %r %r\t' % (quaternion.x, quaternion.y, quaternion.z, quaternion.w))
00154     ## @todo UI for SkyParticles
00155     # For now, manually change the 0 at the end of the file to 1 to
00156     # get the firey solar wind effect.
00157     out.write(' 0 ')
00158     print "Done."
00159     print "[End Racer theme exporter]"
00160 
00161 ## Write an indvidual mesh's data to an open file.
00162 # @param out The output file.
00163 # @param ob The object to write about.
00164 def write_mesh(out, ob):
00165         mesh = ob.getData(mesh=True)
00166         #bounding box
00167         #ignored for now.
00168         #box=ob.boundingBox
00169         #out.write('%f %f %f\t%f %f %f\n' % (box[0].x, box[0].y, box[0].z, box[7].x, box[7].y, box[5].z))
00170         #number of vertices
00171         out.write('%i\n' % (len(mesh.verts)))
00172         #each vertex
00173         for vertex in mesh.verts:
00174             # coordinates
00175             out.write('%r %r %r\n' % (vertex.co.x, vertex.co.y, vertex.co.z))
00176         #number of faces - quads count as two
00177         face_count = 0
00178         for face in mesh.faces:
00179             if len(face.verts) == 4:
00180                 face_count += 2
00181             else:
00182                 face_count += 1
00183         out.write('%i\n' % (face_count))
00184         #each face
00185         for face in mesh.faces:
00186             #Split quads into two triangles.
00187             #For triangles, just use the first.
00188             for vert_index in [0,1,2]:
00189                 write_vert(out, vert_index, face, mesh.faceUV)
00190             if len(face.verts) == 4:
00191                 for vert_index in [0,2,3]:
00192                     write_vert(out, vert_index, face, mesh.faceUV)
00193 
00194 ##Write the output for a particular vertex.
00195 # If the vertex's face does not have texture coordinates, set faceUV to false.
00196 # Texture coordinates will be written as if they were (0,0) in this case.
00197 #@param out the opened file to write to
00198 #@param vert_index an index within the a face
00199 #@param face the face that contains the vertex.
00200 #@param faceUV a boolean value indicating if the face has UV coordinates.
00201 def write_vert(out, vert_index, face, faceUV):
00202     vert = face.verts[vert_index]
00203     #normals
00204     if face.smooth:
00205             out.write('%r %r %r\t' % (vert.no.x, vert.no.y, vert.no.z))
00206     else:
00207             out.write('%r %r %r\t' % (face.no.x, face.no.y, face.no.z))
00208     #uvs
00209     if faceUV:
00210         out.write('%r %r\n' % (face.uv[vert_index].x, 1.0-face.uv[vert_index].y))
00211     else:
00212         #daft uv coordinates to get around the non uv mapped faces.
00213         out.write('0 0\n')
00214     #vertex index
00215     out.write('%i\t' % (vert.index))
00216 
00217 ## Convert a string so it can be loaded by string_from_stream().
00218 # Version 1. Writes 1, the length, and the characters, seperated by spaces.
00219 #@param string string to write.
00220 def format_string(string):
00221     return ('1 %i %s' % (len(string), string))
00222 
00223 print "[Begin Racer theme exporter]"
00224 print "Asking for filename..."
00225 #TODO default to a file in ~/.racer/themes/ so it appears in the editor.
00226 Blender.Window.FileSelector(write, "Export")
00227 

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.