G3D XML Exporter for Blender :
At this time, the main tool to export your model from Blender to the G3D format. It export your model in the "xml-g3d" format (used by Glexemel), thus you'll have to convert it with Glexemel to have a G3D file to use with the game.
Here is the last version of the script. Copy it to a file named g3d_xml_exporter.py, which you will place in your .blender/scripts directory.
The beginning of the script is the documentation about how to use the script and how to create/put texture/animate your model. Please read it.
Current version : 0.1.2
At this time, not updated on the server ([url=http://www.glest.org/files/contrib/tools/]http://www.glest.org/files/contrib/tools/[/url]
has and old version)
Modification since last version :
- Better support of animations with the new frameCount object property
- Only taking the triangle faces
- Displaying a summary of the task in the python console
- Better documentation
#!BPY
"""
Name: 'G3D XML Exporter (.xml -> .g3d)'
Blender: 237
Group: 'Export'
Tooltip: 'Exports to an XML representing the Glest fileformat V4. Use xml2g to convert to .g3d'
"""
###########################################################################
# Glest Model / Texture / Animation Exporter
# for the game Glest that you can find at http://www.glest.org
# copyright 2005-2006 By Vincent Gadoury
# Started Date: December 18 2005 Put Public Decembre 20 2005
# Ver: 0.1.2 (February 12 2006)
# Distributed under the GNU PUBLIC LICENSE
# Based on g3d_support.py by Andreas Becker
# and on glexemel by Jonathan Merritt
###########################################################################
# (I'm sorry for the poor quality of this text,
# I'm not a native English speaker)
#
#INSTALLATION :
# Copy this Script into your .blender\scripts directory
# In Blender, open a Script Window and in the menu choose "Update Menus"
# Export with File->Export->G3D XML Exporter
#
#PREPARE THE MODEL :
# Each of your meshes' faces must be a triangle (3 vertex)
# In Edit mode, you can press Space, edit>faces>Convert to Triangles
# Create and place all your meshes before texturing or animating.
# The X axe is the floor and the Y axe is the elevation. The center
# of the Blender's 3D space will be the center of you object, at the
# ground level. Before exporting your model, apply the transformations
# to each of your mesh (In the 3D space window's menu :
# Object->Clear/Apply->Apply ... )
# This will place the center of your mesh (the yellow or pink dot)
# at the center of the Blender 3D space.
#
# Before doing any texturing or animation, try to export your model and
# check your python console (the terminal which opens Blender) to see
# if there is a warning about the number of used faces in the summary.
# If there is, try to delete "false" faces (which use only 2 vertices)
# and transform all your faces in triangle.
#
#EXPORTING :
# You MUST select the meshes you want to export.
# Only meshes are exported among the selected objects.
#
# The diffuse color of the mesh will be it's (first) material's color.
# The opacity of the mesh will be it's material's alpha.
# Double sided property of the mesh is exported (see F9).
# If you want to use custom color in the texture, your mesh must be
# double sided. If you don't want custom color to be activated for the
# mesh, add a new boolean property to it (F4) named customColor and with
# the false value. Custom Color set if the alpha of the texture is
# replaced for the color of the player.
#
#TEXTURING :
# To use a texture for the mesh, you must add a texture at the FIRST
# position to the mesh's material. The texture type must be 'image'
# and you should insert the real image. In fact, only the filename
# of the image is used. After having mapped the image on the
# mesh with UV Face selection, you don't need anymore to "stick"
# the coordinates to the vertices.
# Your texture's format must be TGA, without compression and with the origin
# at the bottom left position. The side of the image must be a multiple
# of 2. In practice, you must use only one image for all your meshes.
#
#ANIMATING :
# It was only tested with the 'absolute vertex key' method, but it should
# work with all the Blender's animation methods.
# I recommend 'relative vertex key' if you want to modify you mesh later.
# Don't forget to APPLY TRANSFORMATIONS before adding vertex keys.
# You must add a "frameCount" property to the objects that are animated.
# It's and Integer property (F4) name frameCount which contains the
# desired number of frames of the object, starting at frame 1.
# If and object don't have the "frameCount" property, only the first frame
# will be exported.
#
# LINKS :
# Absolute vertex key animation method :
# http://download.blender.org/documentation/htmlI/ch15.html
# Map an image on your mesh with UV Face selection :
# http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro/UV_Map_Basics
# http://download.blender.org/documentation/htmlI/ch11s05.html
#
#CONVERT TO .G3D
# Use the xml2g program of the glexemel tool at
# http://www.glest.org/files/contrib/tools/
# Syntax : ./xml2g ExportedFile.xml DesiredFile.g3d
#
# WARNING : The current version of this program don't seems to work...
# You'll have to add the lines (if they aren't there) :
# #pragma pack(push, 1)
# near of the beginning of the file "g3dv4.h" of glexemel and
# #pragma pack(pop)
# before the #endif
#
#
#ChangeLog :
#
# 0.1.2 :
# Correcting an execution bug
# Adding the "frameCount" property for each object
# Using only the triangle faces for the index count
# (problems of linking between index and vertex may occur)
# Adding the summary display in the python console
# Adding a better documentation
# 0.1.1b :
# Adding a vertex duplication method to correct the vertex UV problem.
# Correcting NMVert.uvco misuse
#
#ToDo:
# More tests...
# More validations...
# Warn the user if there is something wrong, e.g. :
# - no mesh among the selected objects;
# - texture without uvco;
# - no material on a mesh.
# Test and correct the theoric linking problem if faces are ignored...
#
#Contributions :
# « pseudonym404 » : NMVert.uvco issue and solutions
#
###############################################################################
import Blender
from Blender import NMesh
from Blender import sys as bsys
seenindex=set()
uvlist=[]
# This list will contain the associative "real" vertex of each duplicated vertex
# in ascendant order.
# The fisrt "vertex" of the list (newvertices[0]) has the index len(mesh.verts)
newvertices=[]
currentnewvertex=0
#This list will contain all the "new" faces' indices
newindices=[]
def icmp(x,y):
return cmp(x,y)
def notseenindex(index):
seen = index in seenindex
if not seen: seenindex.add(index)
return not seen
def write_obj(filepath):
out = file(filepath, 'w')
print("-----------------------------------------------")
#Header
out.write( '<?xml version="1.0" encoding="ASCII" ?>\n' )
out.write( '<!DOCTYPE G3D SYSTEM "g3d.dtd">\n' )
out.write( '<!-- \n' )
out.write( ' This file is an XML encoding of a G3D binary\n' )
out.write( ' file. The XML format is by Jonathan Merritt\n' )
out.write( ' (not yet accepted as part of Glest!).\n' )
out.write( ' The file was exported from Blender with the\n' )
out.write( ' G3D-XML Exporter script by Vincent Gadoury.\n' )
out.write( '-->\n' )
out.write( '<G3D version="4">\n' )
objects = Blender.Object.GetSelected()
print("Exporting %i selected objects to XML-G3D format..." %(len(objects)))
#FOR EACH MESH
lobjects = range( 0 , len(objects) )
for iobj in lobjects:
object = objects[iobj]
mesh = object.getData()
# Skip the object if it's not a mesh
if type(mesh) != Blender.Types.NMeshType :
continue
#Clear the lists
seenindex.clear()
uvlist[:]=[]
newvertices[:]=[]
newindices[:]=[]
currentnewvertex=(len(mesh.verts))
#Find some properties of the mesh
image = None
textureName = ''
diffuseTexture = 'false'
opacity = 1
diffuseColor = [ 1.0, 1.0, 1.0 ]
#Find if the mesh has a material and a texture
# (opacity, diffuseColor, diffuseTexture, textureName)
if len(mesh.materials) > 0:
material = mesh.materials[0]
opacity = material.alpha
diffuseColor[0] = material.rgbCol[0]
diffuseColor[1] = material.rgbCol[1]
diffuseColor[2] = material.rgbCol[2]
if material.getTextures()[0]:
image = material.getTextures()[0].tex.getImage()
if image:
textureName = image.getFilename()
textureName = textureName.split('/')[-1] #get only the filename
diffuseTexture = 'true'
#End material and texture
#TwoSided
if mesh.mode & NMesh.Modes['TWOSIDED']:
twoSided='true'
else:
twoSided='false'
#CustomColor
customColor = 1
try:
customColor = object.getProperty('customColor').data
except AttributeError:
customColor = customColor
if customColor:
customColor = 'true'
else:
customColor = 'false'
#Real face count (only use triangle)
realFaceCount = 0
for face in mesh.faces:
if (len(face.v) == 3):
realFaceCount += 1
#Frames number
frameCount = 1
try:
frameCount = object.getProperty('frameCount').data
except AttributeError:
frameCount = 1
# TRANSFERING FACE TEXTURE COORD. TO VERTEX TEXTURE COORD.
# and duplicating vertex associated to different uvco
if textureName == '': #THERE IS NO TEXTURE
#create the index list, don't care of newvertices
for face in mesh.faces:
faceindices = []
if (len(face.v) == 3):
for vert in face.v:
faceindices.append(vert.index)
newindices.append(faceindices[0:3])
else: # THERE IS A TEXTURE
uvlist[:] = [[0]*3 for i in range( len(mesh.verts) )]
for face in mesh.faces:
faceindices = []
if (len(face.v) == 3):
for i in range(len(face.uv)):
vindex = face.v[i].index
if notseenindex(vindex):
uvlist[vindex] = [vindex, face.uv[i][0], face.uv[i][1]]
elif uvlist[vindex][1] != face.uv[i][0] or \
uvlist[vindex][2] != face.uv[i][1]:
#debug: print("dif: [%f,%f] et [%f,%f]" %( uvlist[vindex][1], face.uv[i][0], uvlist[vindex][2], face.uv[i][1] ))
#Create a new "entry" for an existing vertex
newvertices.append(vindex)
uvlist.append([currentnewvertex, face.uv[i][0], face.uv[i][1]])
vindex = currentnewvertex
currentnewvertex += 1
faceindices.append(vindex)
newindices.append(faceindices[0:3])
#End texture and vertex copy
# ---- BEGINNING OF THE WRITING OF THE FILE ----
#MESH HEADER
out.write( '\n<Mesh \n' )
out.write( ' name="%s" \n' % (mesh.name) )
out.write( ' frameCount="%i" \n' % ( frameCount ) )
out.write( ' vertexCount="%i" \n' % (len(mesh.verts) + len(newvertices)) )
out.write( ' indexCount="%i" \n' % ( realFaceCount * 3 ) )
out.write( ' specularPower="0" \n' )
out.write( ' opacity="%f" \n' % (opacity) )
out.write( ' twoSided="%s" \n' % ( twoSided ) )
out.write( ' customColor="%s" \n' % ( customColor ) )
out.write( ' diffuseTexture="%s" > \n' % ( diffuseTexture ) )
#DIFFUSE
out.write( ' <Diffuse>\n <Color r="%f" g="%f" b="%f" />\n </Diffuse>\n' % ( diffuseColor[0], diffuseColor[1], diffuseColor[2] ) )
#SPECULAR
out.write( ' <!-- UNUSED -->\n <Specular><Color r="0" g="0" b="0" /></Specular>\n' )
#TEXTURE
if textureName == '':
out.write( ' <!-- NO TEXTURE -->\n' )
else:
out.write( ' <Texture name="%s" />\n' % (textureName) )
#For each FRAME
#VERTICES
l = range( 1 , frameCount+1 )
for frame in l:
Blender.Set('curframe', frame)
fmesh = Blender.Object.GetSelected()[iobj].getData()
out.write( ' <Vertices frame="%i">\n' % ( frame ) )
# "real" vertex
for vert in fmesh.verts:
out.write( ' <Vertex x="%f" y="%f" z="%f" />\n' % (vert.co.x, vert.co.y, vert.co.z) )
# "linked" new vertex
for ind in newvertices:
out.write( ' <Vertex x="%f" y="%f" z="%f" />\n' % \
(fmesh.verts[ind].co.x, fmesh.verts[ind].co.y, fmesh.verts[ind].co.z) )
out.write( ' </Vertices>\n' )
#NORMALS
l = range( 1 , frameCount+1 )
for frame in l:
Blender.Set('curframe', frame)
fmesh = Blender.Object.GetSelected()[iobj].getData()
out.write( ' <Normals frame="%i">\n' % ( frame ) )
# "real" vertex
for vert in fmesh.verts:
out.write( ' <Normal x="%f" y="%f" z="%f" />\n' % (vert.no.x, vert.no.y, vert.no.z) )
# "linked" new vertex
for ind in newvertices:
out.write( ' <Normal x="%f" y="%f" z="%f" />\n' % \
(fmesh.verts[ind].no.x, fmesh.verts[ind].no.y, fmesh.verts[ind].no.z) )
out.write( ' </Normals>\n' )
#End FRAMES
#TEXTURES COORDS
if textureName == '':
out.write( ' <!-- NO TEXTURE COORDS -->\n' )
else:
out.write( ' <TexCoords>\n' )
#write list
for uv in uvlist:
out.write( ' <ST s="%f" t="%f" />\n' % (uv[1], uv[2]) )
out.write( ' </TexCoords>\n' )
#INDICES
out.write( ' <Indices>\n' )
for face in newindices:
for vert in face:
out.write( ' <Ix i="%i" />\n' % (vert) )
out.write('\n')
out.write( ' </Indices>\n' )
#END MESH
out.write( '</Mesh>\n' )
#Printing a summary of the mesh to the output
print("\nObject : %s" %(object.getName() ) )
print("Mesh : %s" %(mesh.name) )
print("%i frames" % ( frameCount ) )
print("%i vertices" % (len(mesh.verts) + len(newvertices)) )
print("%i exported faces (%i real faces)" % (realFaceCount, len(mesh.faces) ) )
if realFaceCount != len(mesh.faces) :
print("WARNING : some faces have been ignored (not triangle)\n" +
" Errors may occur with faces and indices linking..." )
print("%i indices" % (realFaceCount * 3) )
print("opacity : %f" % (opacity) )
print("two sided : %s" % (twoSided) )
print("custom color : %s" % (customColor) )
print("Use a diffuse texture : %s" % (diffuseTexture) )
if textureName != '':
print( " texture name : %s" % (textureName) )
print("diffuse color : %f,%f,%f" % ( diffuseColor[0], diffuseColor[1], diffuseColor[2] ) )
print("Number of new vertices to fit uv mapping : %i\n" % ( len(newvertices) ) )
#FOOTER
out.write( '</G3D>\n' )
out.close()
#END write_obj
# file selector based on ac3d_export.py by Willian P. Germano
# File Selector callback:
def fs_callback(filename):
if not filename.endswith('.xml'): filename = '%s.xml' % filename
if bsys.exists(filename):
if Blender.Draw.PupMenu('OVERWRITE?%t|File exists') != 1:
return
write_obj(filename)
OBJS = Blender.Object.GetSelected()
if not OBJS:
Blender.Draw.PupMenu('ERROR: no objects selected')
else:
fname = bsys.makename(ext=".xml")
Blender.Window.FileSelector(fs_callback, "Export XML-G3D", fname)
#Blender.Window.FileSelector(write_obj, "Export")
If you find any bug or problem with this script, or if you think about an improvement, please reply to this topic.