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
Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.