- added glexemel to svn and CMake builds

- updated the G3D import script to properly bring in animations into Blender!
This commit is contained in:
Mark Vejvoda 2011-01-14 06:56:19 +00:00
parent a8f982ad2b
commit ada4140d84
13 changed files with 2613 additions and 3 deletions

View File

@ -130,9 +130,15 @@ ENDIF()
ADD_SUBDIRECTORY( source/shared_lib )
ADD_SUBDIRECTORY( source/glest_game )
ADD_SUBDIRECTORY( source/glest_map_editor )
ADD_SUBDIRECTORY( source/g3d_viewer )
ADD_SUBDIRECTORY( source/configurator )
#if(wxWidgets_FOUND)
ADD_SUBDIRECTORY( source/glest_map_editor )
ADD_SUBDIRECTORY( source/g3d_viewer )
ADD_SUBDIRECTORY( source/configurator )
#else()
# MESSAGE(STATUS "WARNING... the following game tools will NOT be built since we cannot find wxWidgets on this machine")
# MESSAGE(STATUS "map editor, g3d viewer, configurator")
#endif()
ADD_SUBDIRECTORY( source/tools/glexemel )
IF(APPLE)
include(mk/macosx/CMakeLists.txt)

View File

@ -0,0 +1,13 @@
# glexemel
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/source/tools/glexemel)
add_executable(g2xml g2xml.c g3dv4.h)
find_package(LibXml2)
IF(LIBXML2_FOUND)
include_directories(${LIBXML2_INCLUDE_DIR})
add_executable(xml2g xml2g.c g3dv4.h)
target_link_libraries(xml2g ${LIBXML2_LIBRARIES})
ELSE()
MESSAGE(STATUS "WARNING... xml2g will NOT be built since we cannot find libXml2 on this machine")
ENDIF()

View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@ -0,0 +1,11 @@
Sorry - no autotools support (yet!).
You will need:
1) GNU Make (or an equivalent).
2) An ANSI C compiler (eg: gcc).
3) libxml2 (http://xmlsoft.org) - the XML parser for GNOME.
Just copy Makefile.linux to Makefile:
$ cp Makefile.linux Makefile
Then edit Makefile to suit your needs (it's *very* simple).

View File

@ -0,0 +1,30 @@
# You need to edit this Makefile for your specific platform
# CC is the name of your C compiler
CC=gcc
# CFLAGS are the flags that you want to pass to the C compiler
CFLAGS=-Wall -ansi -pedantic
# IDIRS specify the include directories to use
IDIRS=-I/usr/include/libxml2
# LIBS specify the libraries (libxml2)
LIBS=-lxml2
# INSTALLDIR specifies the directory to which the binaries should be installed
INSTALLDIR=/usr/local/bin
# CP specifies the copy command for the system
CP=cp
all: g2xml xml2g
g2xml:
${CC} ${CFLAGS} ${IDIRS} g2xml.c -o g2xml ${LIBS}
xml2g:
${CC} ${CFLAGS} ${IDIRS} xml2g.c -o xml2g ${LIBS}
install:
${CP} g2xml ${INSTALLDIR}/.
${CP} xml2g ${INSTALLDIR}/.
${CP} g3d.dtd ${INSTALLDIR}/.
clean:
rm -f g2xml xml2g

View File

@ -0,0 +1,13 @@
=== GLEXEMEL ===
An XML format for the G3D file format used by the game Glest:
http://www.glest.org
This XML format allows for exporters to create XML file descriptions that
can then be converted to binary.
Note: This package is not yet part of Glest, although it is offered for
inclusion should the developers wish.
Copyright (C) Jonathan Merritt 2005.
This package may be distributed under the terms of the GNU General Public
License (see the file COPYING for more details).

View File

@ -0,0 +1,362 @@
/**
* File: g2xml.c
* Written: Jonathan Merritt <jmerritt@warpax.com>
*
* Description:
* Converts G3D format files into an XML representation.
*
* Copyright (C) Jonathan Merritt 2005.
* This file may be distributed under the terms of the GNU General Public
* License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "g3dv4.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/**
* Forward function declarations.
*/
int g3d2xml(FILE *infile, FILE *outfile);
void usage(char *execname);
/**
* Program entry point.
*
* @param argc: Number of arguments passed to the program.
* @param argv: Array of string arguments.
*
* @returns: EXIT_SUCCESS or EXIT_FAILURE depending upon success or failure
* of the conversion to XML.
*/
int main(int argc, char **argv)
{
char *infilename, *outfilename;
FILE *infile, *outfile;
int successFlag;
/* parse command line arguments */
if (argc != 3)
{
usage(argv[0]);
return (EXIT_FAILURE);
}
else
{
infilename = argv[1];
outfilename = argv[2];
}
/* attempt to open the input and output files */
infile = fopen(infilename, "rb");
if (infile == NULL)
{
printf("Could not open file \"%s\" for binary reading.\n",
infilename);
return (EXIT_FAILURE);
}
outfile = fopen(outfilename, "w");
if (outfile == NULL)
{
printf("Could not open file \"%s\" for writing.\n",
outfilename);
fclose(infile);
return (EXIT_FAILURE);
}
/* perform the XML conversion */
successFlag = g3d2xml(infile, outfile);
/* close the two files */
fclose(infile);
fclose(outfile);
/* return a success or failure flag */
if (successFlag)
return (EXIT_SUCCESS);
else
return (EXIT_FAILURE);
}
/**
* Prints a "Usage:" string for the program.
*
* @param execname: Executable name of the program.
*/
void usage(char *execname)
{
printf("Usage:\n");
printf(" %s infile.g3d outfile.xml\n", execname);
return;
}
/**
* Performs the conversion from the G3D file format to XML.
*
* @param infile: G3D binary file, opened as "rb", for input.
* @param outfile: Text file, opened as "w", for XML output.
*
* @returns: TRUE if conversion to XML was successful, and FALSE otherwise.
*/
int g3d2xml(FILE *infile, FILE *outfile)
{
struct FileHeader fileHeader;
struct ModelHeader modelHeader;
struct MeshHeader meshHeader;
size_t nBytes;
uint8 textureName[NAMESIZE];
float32 *fdata;
uint32 *idata;
int ii, jj, kk;
/* read in the FileHeader */
nBytes = sizeof(struct FileHeader);
if (fread(&fileHeader, nBytes, 1, infile) != 1)
{
printf("Could not read file header!\n");
return FALSE;
}
if (strncmp((char*)fileHeader.id, "G3D", 3) != 0)
{
printf("Expected \"G3D\" id was not found!\n");
return FALSE;
}
if (fileHeader.version != 4)
{
printf("Version 4 expected, but version %d found!\n",
fileHeader.version);
return FALSE;
}
fprintf(outfile, "<?xml version=\"1.0\" encoding=\"ASCII\" ?>\n");
fprintf(outfile, "<!DOCTYPE G3D SYSTEM \"g3d.dtd\">\n");
fprintf(outfile, "<!--\n");
fprintf(outfile, "\tThis file is an XML encoding of a G3D binary\n");
fprintf(outfile, "\tfile. The XML format is by Jonathan Merritt\n");
fprintf(outfile, "\t(not yet accepted as part of Glest!).\n");
fprintf(outfile, "-->\n");
fprintf(outfile, "<G3D version=\"%d\">\n", fileHeader.version);
/* read in the ModelHeader */
nBytes = sizeof(struct ModelHeader);
if (fread(&modelHeader, nBytes, 1, infile) != 1)
{
printf("Could not read model header!\n");
return FALSE;
}
if (modelHeader.type != mtMorphMesh)
{
printf("Unrecognized mesh type!\n");
return FALSE;
}
/* read in the meshes */
for (ii = 0; ii < modelHeader.meshCount; ii++)
{
/* read in the MeshHeader */
nBytes = sizeof(struct MeshHeader);
if (fread(&meshHeader, nBytes, 1, infile) != 1)
{
printf("Could not read mesh header!\n");
return FALSE;
}
/* write out XML mesh header */
fprintf(outfile, "\t<Mesh name=\"%s\" ", meshHeader.name);
fprintf(outfile, "frameCount=\"%d\" ", meshHeader.frameCount);
fprintf(outfile, "vertexCount=\"%d\" ",
meshHeader.vertexCount);
fprintf(outfile, "indexCount=\"%d\" ", meshHeader.indexCount);
fprintf(outfile, "specularPower=\"%f\" ",
meshHeader.specularPower);
fprintf(outfile, "opacity=\"%f\" ", meshHeader.opacity);
if (meshHeader.properties & (0x1 << mpfTwoSided))
fprintf(outfile, "twoSided=\"true\" ");
else
fprintf(outfile, "twoSided=\"false\" ");
if (meshHeader.properties & (0x1 << mpfCustomColor))
fprintf(outfile, "customColor=\"true\" ");
else
fprintf(outfile, "customColor=\"false\" ");
if (meshHeader.textures)
fprintf(outfile, "diffuseTexture=\"true\"");
else
fprintf(outfile, "diffuseTexture=\"false\"");
fprintf(outfile, ">\n");
/* write out diffuse and specular colors */
fprintf(outfile, "\t\t<Diffuse>\n");
fprintf(outfile, "\t\t\t<Color r=\"%f\" g=\"%f\" b=\"%f\"/>\n",
meshHeader.diffuseColor[0],
meshHeader.diffuseColor[1],
meshHeader.diffuseColor[2]);
fprintf(outfile, "\t\t</Diffuse>\n");
fprintf(outfile, "\t\t<Specular>\n");
fprintf(outfile, "\t\t\t<Color r=\"%f\" g=\"%f\" b=\"%f\"/>\n",
meshHeader.specularColor[0],
meshHeader.specularColor[1],
meshHeader.specularColor[2]);
fprintf(outfile, "\t\t</Specular>\n");
/* read / write the texture name if present */
if (meshHeader.textures)
{
nBytes = sizeof(textureName);
if (fread(&textureName, nBytes, 1, infile) != 1)
{
printf("Could not read texture name!\n");
return FALSE;
}
fprintf(outfile, "\t\t<Texture name=\"%s\"/>\n",
textureName);
}
/* read / write each set of vertex data */
for (jj=0; jj < meshHeader.frameCount; jj++)
{
nBytes = sizeof(float32)*meshHeader.vertexCount*3;
fdata = malloc(nBytes);
if (fdata == NULL)
{
printf("Could not allocate buffer!\n");
return FALSE;
}
if (fread(fdata, nBytes, 1, infile) != 1)
{
printf("Could not read vertex data!\n");
free(fdata);
return FALSE;
}
fprintf(outfile, "\t\t<Vertices frame=\"%d\">\n",
jj);
for (kk=0; kk < meshHeader.vertexCount; kk++)
{
fprintf(outfile, "\t\t\t<Vertex ");
/*fprintf(outfile, "i=\"%d\" ",
kk);*/
fprintf(outfile, "x=\"%f\" ",
fdata[3*kk]);
fprintf(outfile, "y=\"%f\" ",
fdata[3*kk+1]);
fprintf(outfile, "z=\"%f\"/>\n",
fdata[3*kk+2]);
}
fprintf(outfile, "\t\t</Vertices>\n");
free(fdata);
}
/* read / write each set of normal data */
for (jj=0; jj < meshHeader.frameCount; jj++)
{
nBytes = sizeof(float32)*meshHeader.vertexCount*3;
fdata = malloc(nBytes);
if (fdata == NULL)
{
printf("Could not allocate buffer!\n");
return FALSE;
}
if (fread(fdata, nBytes, 1, infile) != 1)
{
printf("Could not read normal data!\n");
free(fdata);
return FALSE;
}
fprintf(outfile, "\t\t<Normals frame=\"%d\">\n",
jj);
for (kk=0; kk < meshHeader.vertexCount; kk++)
{
fprintf(outfile, "\t\t\t<Normal ");
/*fprintf(outfile, "i=\"%d\" ",
kk);*/
fprintf(outfile, "x=\"%f\" ",
fdata[3*kk]);
fprintf(outfile, "y=\"%f\" ",
fdata[3*kk+1]);
fprintf(outfile, "z=\"%f\"/>\n",
fdata[3*kk+2]);
}
fprintf(outfile, "\t\t</Normals>\n");
free(fdata);
}
/* read / write texture coordinates */
if (meshHeader.textures)
{
nBytes = sizeof(float32)*meshHeader.vertexCount*2;
fdata = malloc(nBytes);
if (fdata == NULL)
{
printf("Could not allocate buffer!\n");
return FALSE;
}
if (fread(fdata, nBytes, 1, infile) != 1)
{
printf("Could not read texture coords!\n");
free(fdata);
return FALSE;
}
fprintf(outfile, "\t\t<TexCoords>\n");
for (kk=0; kk < meshHeader.vertexCount; kk++)
{
fprintf(outfile, "\t\t\t<ST ");
/*fprintf(outfile, "i=\"%d\" ",
kk);*/
fprintf(outfile, "s=\"%f\" ",
fdata[2*kk]);
fprintf(outfile, "t=\"%f\"/>\n",
fdata[2*kk+1]);
}
fprintf(outfile, "\t\t</TexCoords>\n");
free(fdata);
}
/* read / write face indices */
nBytes = sizeof(uint32)*meshHeader.indexCount;
idata = malloc(nBytes);
if (idata == NULL)
{
printf("Could not allocate buffer!\n");
return FALSE;
}
if (fread(idata, nBytes, 1, infile) != 1)
{
printf("Could not read indexes!\n");
free(idata);
return FALSE;
}
fprintf(outfile, "\t\t<Indices>\n");
for (kk=0; kk < meshHeader.indexCount; kk++)
{
fprintf(outfile, "\t\t\t<Ix i=\"%d\"/>\n",
idata[kk]);
}
fprintf(outfile, "\t\t</Indices>\n");
free(idata);
fprintf(outfile, "\t</Mesh>\n");
}
fprintf(outfile, "</G3D>\n");
return TRUE;
}

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="ASCII" ?>
<!--
A DTD for describing an XML representation of G3D version 4 files.
Created by Jonathan Merritt <jmerritt@warpax.com>
Copyright (C) Jonathan Merritt 2005.
This DTD may be distributed under the terms of the GNU General Public
License.
-->
<!ELEMENT G3D (Mesh+)>
<!ATTLIST G3D version CDATA #REQUIRED>
<!ELEMENT Mesh
(Diffuse,Specular,Texture?,Vertices+,Normals+,TexCoords?,Indices)>
<!ATTLIST Mesh name CDATA #REQUIRED>
<!ATTLIST Mesh frameCount CDATA #REQUIRED>
<!ATTLIST Mesh vertexCount CDATA #REQUIRED>
<!ATTLIST Mesh indexCount CDATA #REQUIRED>
<!ATTLIST Mesh specularPower CDATA #REQUIRED>
<!ATTLIST Mesh opacity CDATA #REQUIRED>
<!ATTLIST Mesh twoSided (true|false) #REQUIRED>
<!ATTLIST Mesh customColor (true|false) #REQUIRED>
<!ATTLIST Mesh diffuseTexture (true|false) #REQUIRED>
<!ELEMENT Diffuse (Color)>
<!ELEMENT Specular (Color)>
<!ELEMENT Color EMPTY>
<!ATTLIST Color r CDATA #REQUIRED>
<!ATTLIST Color g CDATA #REQUIRED>
<!ATTLIST Color b CDATA #REQUIRED>
<!ELEMENT Texture EMPTY>
<!ATTLIST Texture name CDATA #REQUIRED>
<!ELEMENT Vertices (Vertex+)>
<!ATTLIST Vertices frame CDATA #IMPLIED>
<!ELEMENT Vertex EMPTY>
<!ATTLIST Vertex x CDATA #REQUIRED>
<!ATTLIST Vertex y CDATA #REQUIRED>
<!ATTLIST Vertex z CDATA #REQUIRED>
<!ELEMENT Normals (Normal+)>
<!ATTLIST Normals frame CDATA #IMPLIED>
<!ELEMENT Normal EMPTY>
<!ATTLIST Normal x CDATA #REQUIRED>
<!ATTLIST Normal y CDATA #REQUIRED>
<!ATTLIST Normal z CDATA #REQUIRED>
<!ELEMENT TexCoords (ST+)>
<!ELEMENT ST EMPTY>
<!ATTLIST ST s CDATA #REQUIRED>
<!ATTLIST ST t CDATA #REQUIRED>
<!ELEMENT Indices (Ix+)>
<!ELEMENT Ix EMPTY>
<!ATTLIST Ix i CDATA #REQUIRED>

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,507 @@
#!BPY
"""
Name: 'G3D Fileformat Im/Exporter'
Blender: 237
Group: 'Wizards'
Tooltip: 'Imports and Exports the Glest fileformat V3/V4 (.g3d)'
"""
###########################################################################
# Glest Model / Texture / UV / Animation Importer and Exporter
# for the Game Glest that u can find http://www.glest.org
# copyright 2005 By Andreas Becker (seltsamuel@yahoo.de)
#
# Updated Jan 2011 by Mark Vejvoda (SoftCoder) to properly import animations
# from G3D into Blender
#
# Started Date: 07 June 2005 Put Public 20 June 2005
# Ver: 0.01 Beta Exporter ripped off because work in Progress
# Distributed under the GNU PUBLIC LICENSE for www.megaglest.org and glest.org
###########################################################################
#NOTE:
# Copy this Script AND g3d_logo.png into .Blender\scripts
# directory then start inside blender Scripts window
# "Update Menus" after that this Script here is accesible
# as 'Wizards' G3d Fileformat Im/Exporter
#ToDo:
#Exporter Bughunt he will be rejoined next release
#Maybe Integrate UV Painter to Generate UVMaps from Blender Material and procedural Textures
#will be nice to paint wireframe too, so that one can easyly load into Paintprogram and see where to paint
#(Already possible through Blender functions so at the end of the list :-) )
###########################################################################
"""
Here an explanation of the V4 Format found at www.glest.org
================================
1. DATA TYPES
================================
G3D files use the following data types:
uint8: 8 bit unsigned integer
uint16: 16 bit unsigned integer
uint32: 32 bit unsigned integer
float32: 32 bit floating point
================================
2. OVERALL STRUCTURE
================================
- File header
- Model header
- Mesh header
- Texture names
- Mesh data
================================
2. FILE HEADER
================================
Code:
struct FileHeader{
uint8 id[3];
uint8 version;
};
This header is shared among all the versions of G3D, it identifies this file as a G3D model and provides information of the version.
id: must be "G3D"
version: must be 4, in binary (not '4')
================================
3. MODEL HEADER
================================
Code:
struct ModelHeader{
uint16 meshCount;
uint8 type;
};
meshCount: number of meshes in this model
type: must be 0
================================
4. MESH HEADER
================================
There is a mesh header for each mesh, there must be "meshCount" headers in a file but they are not consecutive, texture names and mesh data are stored in between.
Code:
struct MeshHeader{
uint8 name[64];
uint32 frameCount;
uint32 vertexCount;
uint32 indexCount;
float32 diffuseColor[3];
float32 specularColor[3];
float32 specularPower;
float32 opacity;
uint32 properties;
uint32 textures;
};
name: name of the mesh
frameCount: number of keyframes in this mesh
vertexCount: number of vertices in each frame
indexCount: number of indices in this mesh (the number of triangles is indexCount/3)
diffuseColor: RGB diffuse color
specularColor: RGB specular color (currently unused)
specularPower: specular power (currently unused)
properties: property flags
Code:
enum MeshPropertyFlag{
mpfTwoSided= 1,
mpfCustomColor= 2,
};
mpfTwoSided: meshes in this mesh are rendered by both sides, if this flag is not present only "counter clockwise" faces are rendered
mpfCustomColor: alpha in this model is replaced by a custom color, usually the player color
textures: texture flags, only 0x1 is currently used, indicating that there is a diffuse texture in this mesh.
================================
4. TEXTURE NAMES
================================
A list of uint8[64] texture name values. One for each texture in the mesh. If there are no textures in the mesh no texture names are present. In practice since Glest only uses 1 texture for each mesh the number of texture names should be 0 or 1.
================================
5. MESH DATA
================================
After each mesh header and texture names the mesh data is placed.
vertices: frameCount * vertexCount * 3, float32 values representing the x, y, z vertex coords for all frames
normals: frameCount * vertexCount * 3, float32 values representing the x, y, z normal coords for all frames
texture coords: vertexCount * 2, float32 values representing the s, t tex coords for all frames (only present if the mesh has 1 texture at least)
indices: indexCount, uint32 values representing the indices
"""
###########################################################################
# Importing Structures needed (must later verify if i need them really all)
###########################################################################
import Blender
from Blender import NMesh, Object, Scene
from Blender.BGL import *
from Blender.Draw import *
from Blender.Window import *
from Blender.Image import *
import sys, struct, string, types
from types import *
import os
from os import path
from os.path import dirname
###########################################################################
# Variables that are better Global to handle
###########################################################################
imported = [] #List of all imported Objects
toexport = [] #List of Objects to export (actually only meshes)
sceneID = None #Points to the active Blender Scene
###########################################################################
# Declaring Structures of G3D Format
###########################################################################
class G3DHeader: #Read first 4 Bytes of file should be G3D + Versionnumber
binary_format = "<3cB"
def __init__(self, fileID):
temp = fileID.read(struct.calcsize(self.binary_format))
data = struct.unpack(self.binary_format,temp)
self.id = "".join(data[0:3])
self.version = data[3]
class G3DModelHeaderv3: #Read Modelheader in V3 there is only the number of Meshes in file
binary_format = "<I"
def __init__(self, fileID):
temp = fileID.read(struct.calcsize(self.binary_format))
data = struct.unpack(self.binary_format,temp)
self.meshcount = data[0]
class G3DModelHeaderv4: #Read Modelheader: Number of Meshes and Meshtype (must be 0)
binary_format = "<HB"
def __init__(self, fileID):
temp = fileID.read(struct.calcsize(self.binary_format))
data = struct.unpack(self.binary_format,temp)
self.meshcount = data[0]
self.mtype = data[1]
class G3DMeshHeaderv3: #Read Meshheader
binary_format = "<7I64c"
def __init__(self,fileID):
temp = fileID.read(struct.calcsize(self.binary_format))
data = struct.unpack(self.binary_format,temp)
self.framecount = data[0] #Framecount = Number of Animationsteps
self.normalframecount= data[1] #Number of Normal Frames actualli equal to Framecount
self.texturecoordframecount= data[2]#Number of Frames of Texturecoordinates seems everytime to be 1
self.colorframecount= data[3] #Number of Frames of Colors seems everytime to be 1
self.vertexcount= data[4] #Number of Vertices in each Frame
self.indexcount= data[5] #Number of Indices in Mesh (Triangles = Indexcount/3)
self.properties= data[6] #Property flags
if self.properties & 1: #PropertyBit is Mesh Textured ?
self.hastexture = False
self.texturefilename = None
else:
self.texturefilename = "".join([x for x in data[7:-1] if x in string.printable])
self.hastexture = True
if self.properties & 2: #PropertyBit is Mesh TwoSided ?
self.istwosided = True
else:
self.istwosided = False
if self.properties & 4: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
self.customalpha = True
else:
self.customalpha = False
class G3DMeshHeaderv4: #Read Meshheader
binary_format = "<64c3I8f2I"
texname_format = "<64c"
def __init__(self,fileID):
temp = fileID.read(struct.calcsize(self.binary_format))
data = struct.unpack(self.binary_format,temp)
self.meshname = "".join([x for x in data[0:64] if x in string.printable]) #Name of Mesh every Char is a String on his own
self.framecount = data[64] #Framecount = Number of Animationsteps
self.vertexcount = data[65] #Number of Vertices in each Frame
self.indexcount = data[66] #Number of Indices in Mesh (Triangles = Indexcount/3)
self.diffusecolor = data[67:70] #RGB diffuse color
self.specularcolor = data[70:73] #RGB specular color (unused)
self.specularpower = data[73] #Specular power (unused)
self.opacity = data[74] #Opacity
self.properties= data[75] #Property flags
self.textures = data[76] #Texture flag
if self.properties & 1: #PropertyBit is Mesh TwoSided ?
self.istwosided = True
else:
self.istwosided = False
if self.properties & 2: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
self.customalpha = True
else:
self.customalpha = False
if self.textures & 1: #PropertyBit is Mesh Textured ?
temp = fileID.read(struct.calcsize(self.texname_format))
data = struct.unpack(self.texname_format,temp)
self.texturefilename = "".join([x for x in data[0:-1] if x in string.printable])
self.hastexture = True
else:
self.hastexture = False
self.texturefilename = None
class G3DMeshdataV3: #Calculate and read the Mesh Datapack
def __init__(self,fileID,header):
#Calculation of the Meshdatasize to load because its variable
#Animationframes * Vertices per Animation * 3 (Each Point are 3 Float X Y Z Coordinates)
vertex_format = "<%if" % int(header.framecount * header.vertexcount * 3)
#The same for Normals
normals_format = "<%if" % int(header.normalframecount * header.vertexcount * 3)
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
texturecoords_format = "<%if" % int(header.texturecoordframecount * header.vertexcount * 2)
#Colors in format RGBA
colors_format = "<%if" % int(header.colorframecount * 4)
#Indices
indices_format = "<%iI" % int(header.indexcount)
#Load the Meshdata as calculated above
self.vertices = struct.unpack(vertex_format,fileID.read(struct.calcsize(vertex_format)))
self.normals = struct.unpack(normals_format,fileID.read(struct.calcsize(normals_format)))
self.texturecoords = struct.unpack(texturecoords_format,fileID.read(struct.calcsize(texturecoords_format)))
self.colors = struct.unpack(colors_format,fileID.read(struct.calcsize(colors_format)))
self.indices = struct.unpack(indices_format ,fileID.read(struct.calcsize(indices_format)))
class G3DMeshdataV4: #Calculate and read the Mesh Datapack
def __init__(self,fileID,header):
#Calculation of the Meshdatasize to load because its variable
#Animationframes * Points (Vertex) per Animation * 3 (Each Point are 3 Float X Y Z Coordinates)
vertex_format = "<%if" % int(header.framecount * header.vertexcount * 3)
#The same for Normals
normals_format = "<%if" % int(header.framecount * header.vertexcount * 3)
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
texturecoords_format = "<%if" % int(header.vertexcount * 2)
#Indices
indices_format = "<%iI" % int(header.indexcount)
#Load the Meshdata as calculated above
self.vertices = struct.unpack(vertex_format,fileID.read(struct.calcsize(vertex_format)))
self.normals = struct.unpack(normals_format,fileID.read(struct.calcsize(normals_format)))
if header.hastexture:
self.texturecoords = struct.unpack(texturecoords_format,fileID.read(struct.calcsize(texturecoords_format)))
self.indices = struct.unpack(indices_format ,fileID.read(struct.calcsize(indices_format)))
def createMesh(filename,header,data): #Create a Mesh inside Blender
mesh = NMesh.New() #New Mesh
meshobj = Object.New("Mesh",header.meshname) #New Object for the new Mesh
meshobj.link(mesh) #Link the Mesh to the Object
sceneID.link (meshobj) #Link the Object to the actual Scene
uvcoords = []
if header.hastexture: #Load Texture when assigned
texturefile = dirname(filename)+os.sep+header.texturefilename
texture = Load(texturefile)
mesh.hasFaceUV(1) #Because has Texture must have UV Coordinates
mesh.hasVertexUV(1) #UV for each Vertex not only for Face
for x in xrange(0,len(data.texturecoords),2): #Prepare the UVs
uvcoords.append((data.texturecoords[x],data.texturecoords[x+1]))
else: #Has no Texture
texture = None
mesh.hasFaceUV(0)
if header.isv4:
mesh.col(int(255*header.diffusecolor[0]),int(255*header.diffusecolor[1]),int(255*header.diffusecolor[2]),int(255*header.opacity))
if header.istwosided:
mesh.setMode("TwoSided","AutoSmooth") #Added Autosmooth because i think it does no harm
else: #I wonder why TwoSided here takes no effect on Faces ??
mesh.setMode("AutoSmooth") #Same here
y=0
for x in xrange(0,header.vertexcount*3,3): #Get the Vertices and Normals into empty Mesh
vertex=NMesh.Vert() #and load UV coords when needed per Vertex
vertex.co[0] = data.vertices[x]
vertex.co[1] = data.vertices[x+1]
vertex.co[2] = data.vertices[x+2]
vertex.no[0] = data.normals[x]
vertex.no[1] = data.normals[x+1]
vertex.no[2] = data.normals[x+2]
if header.hastexture: ###Have to Check this per Vertex UV### may not be needed
vertex.uvco[0] = data.texturecoords[y] ###but seems to do no harm :-)###
vertex.uvco[1] = data.texturecoords[y+1]
mesh.verts.append(vertex)
y= y+2
for i in xrange(0,len(data.indices),3): #Build Faces into Mesh
face = NMesh.Face()
face.smooth = 1
face.v.append(mesh.verts[data.indices[i]])
face.v.append(mesh.verts[data.indices[i+1]])
face.v.append(mesh.verts[data.indices[i+2]])
if header.hastexture: #Put UV in Faces too and assign Texture when textured
face.uv.append(uvcoords[data.indices[i]])
face.uv.append(uvcoords[data.indices[i+1]])
face.uv.append(uvcoords[data.indices[i+2]])
face.image = texture
if header.istwosided: #Finaly found out how it works :-)
face.mode |= NMesh.FaceModes['TWOSIDE'] #Really Bad documented this works to set all modes
mesh.faces.append(face)
mesh.update() #Update changes to Mesh
objdata = meshobj.getData() #Access Data of the Meshobject only the Obj has Key/IPOData
imported.append(meshobj) #Add to Imported Objects
for x in xrange(header.framecount): #Put in Vertex Positions for Keyanimation
for i in xrange(0,header.vertexcount*3,3):
objdata.verts[i/3].co[0]= data.vertices[x*header.vertexcount*3 + i]
objdata.verts[i/3].co[1]= data.vertices[x*header.vertexcount*3 + i +1]
objdata.verts[i/3].co[2]= data.vertices[x*header.vertexcount*3 + i +2 ]
objdata.update()
mesh.insertKey(x+1,"absolute")
Blender.Set("curframe",x)
# Make the keys animate in the 3d view.
key = mesh.key
key.relative = False
# Add an IPO to teh Key
ipo = Blender.Ipo.New('Key', 'md2')
key.ipo = ipo
# Add a curve to the IPO
curve = ipo.addCurve('Basis')
# Add 2 points to cycle through the frames.
curve.append((1, 0))
curve.append((header.framecount, (header.framecount-1)/10.0))
curve.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
return
###########################################################################
# Import
###########################################################################
def G3DLoader(filename): #Main Import Routine
global imported, sceneID
print "\nNow Importing File : " + filename
fileID = open(filename,"rb")
header = G3DHeader(fileID)
print "\nHeader ID : " + header.id
print "Version : " + str(header.version)
if header.id != "G3D":
print "This is Not a G3D Model File"
fileID.close
return
if header.version not in (3, 4):
print "The Version of this G3D File is not Supported"
fileID.close
return
in_editmode = Blender.Window.EditMode() #Must leave Editmode when active
if in_editmode: Blender.Window.EditMode(0)
sceneID=Scene.getCurrent() #Get active Scene
scenecontext=sceneID.getRenderingContext() #To Access the Start/Endframe its so hidden i searched till i got angry :-)
basename=str(Blender.sys.makename(filename,"",1)) #Generate the Base Filename without Path + extension
imported = []
maxframe=0
if header.version == 3:
modelheader = G3DModelHeaderv3(fileID)
print "Number of Meshes : " + str(modelheader.meshcount)
for x in xrange(modelheader.meshcount):
meshheader = G3DMeshHeaderv3(fileID)
meshheader.isv4 = False
print "\nMesh Number : " + str(x+1)
print "framecount : " + str(meshheader.framecount)
print "normalframecount : " + str(meshheader.normalframecount)
print "texturecoordframecount: " + str(meshheader.texturecoordframecount)
print "colorframecount : " + str(meshheader.colorframecount)
print "pointcount : " + str(meshheader.vertexcount)
print "indexcount : " + str(meshheader.indexcount)
print "texturename : " + str(meshheader.texturefilename)
print "hastexture : " + str(meshheader.hastexture)
print "istwosided : " + str(meshheader.istwosided)
print "customalpha : " + str(meshheader.customalpha)
meshheader.meshname = basename+str(x+1) #Generate Meshname because V3 has none
if meshheader.framecount > maxframe: maxframe = meshheader.framecount #Evaluate the maximal animationsteps
meshdata = G3DMeshdataV3(fileID,meshheader)
createMesh(filename,meshheader,meshdata)
fileID.close
print "Imported Objects: ",imported
scenecontext.startFrame(1) #Yeah finally found this Options :-)
scenecontext.endFrame(maxframe) #Set it correctly to the last Animationstep :-))))
Blender.Set("curframe",1) #Why the Heck are the above Options not here accessible ????
anchor = Object.New("Empty",basename) #Build an "empty" to Parent all meshes to it for easy handling
sceneID.link(anchor) #Link it to current Scene
anchor.makeParent(imported) #Make it Parent for all imported Meshes
anchor.sel = 1 #Select it
if in_editmode: Blender.Window.EditMode(1) # Be nice and restore Editmode when was active
return
if header.version == 4:
modelheader = G3DModelHeaderv4(fileID)
print "Number of Meshes : " + str(modelheader.meshcount)
for x in xrange(modelheader.meshcount):
meshheader = G3DMeshHeaderv4(fileID)
meshheader.isv4 = False
print "\nMesh Number : " + str(x+1)
print "meshname : " + str(meshheader.meshname)
print "framecount : " + str(meshheader.framecount)
print "vertexcount : " + str(meshheader.vertexcount)
print "indexcount : " + str(meshheader.indexcount)
print "diffusecolor : %1.6f %1.6f %1.6f" %meshheader.diffusecolor
print "specularcolor : %1.6f %1.6f %1.6f" %meshheader.specularcolor
print "specularpower : %1.6f" %meshheader.specularpower
print "opacity : %1.6f" %meshheader.opacity
print "properties : " + str(meshheader.properties)
print "textures : " + str(meshheader.textures)
print "texturename : " + str(meshheader.texturefilename)
if len(meshheader.meshname) ==0: #When no Meshname in File Generate one
meshheader.meshname = basename+str(x+1)
if meshheader.framecount > maxframe: maxframe = meshheader.framecount #Evaluate the maximal animationsteps
meshdata = G3DMeshdataV4(fileID,meshheader)
createMesh(filename,meshheader,meshdata)
fileID.close
scenecontext.startFrame(1) #Yeah finally found this Options :-)
scenecontext.endFrame(maxframe) #Set it correctly to the last Animationstep :-))))
Blender.Set("curframe",1) #Why the Heck are the above Options not here accessible ????
anchor = Object.New("Empty",basename) #Build an "empty" to Parent all meshes to it for easy handling
sceneID.link(anchor) #Link it to current Scene
anchor.makeParent(imported) #Make it Parent for all imported Meshes
anchor.sel = 1 #Select it
if in_editmode: Blender.Window.EditMode(1) # Be nice and restore Editmode when was active
print "Created a empty Object as 'Grip' where all imported Objects are parented to"
print "To move the complete Meshes only select this empty Object and move it"
print "All Done, have a good Day :-)\n\n"
return
###########################################################################
# Export (Ripped out its work in Progress will be added soon
###########################################################################
def G3DSaverV3(filename):
return
def G3DSaverV4(filename):
return
###########################################################################
# Complete GUI Part of The Wizard
###########################################################################
# Events that can occure by pressing the Buttons
EVENT_LOADG3D=2
EVENT_SAVEG3DV3=3
EVENT_SAVEG3DV4=4
EVENT_EXIT=100
###########################################################################
# GUI eventhandler
###########################################################################
def event(evt, val):
if evt == Blender.Draw.ESCKEY and not val: Blender.Draw.Exit()
def button_event(evt):
global EVENT_LOAD_G3D,EVENT_SAVE_G3DV3,EVENT_SAVE_G3DV4,EVENT_EXIT
if evt == EVENT_EXIT: Exit()
if evt == EVENT_LOADG3D:
defaultname = os.sep + ".g3d"
#I use this defaultnames only for development to have short ways to models
#if u want make a path to your models here and comment the above defaultname out.
#Be careful to not move the start of the Line because it matters in python
# defaultname="I:/glest/glest_game/techs/magitech/factions/magic/units/magic_armor/models/magic_armor_walking.g3d"
# defaultname="I:/glest/glest_game/techs/magitech/factions/.g3d"
# defaultname="I:/entwicklung/blenderexport/test/swordman_sample/swordman.g3d"
FileSelector(G3DLoader, "G3D to load", defaultname)
Blender.Redraw()
if evt==EVENT_SAVEG3DV3:
defaultname = Blender.sys.makename(Blender.Get("filename"),".g3d",1)
FileSelector(G3DSaverV3, "G3D V3 to save", defaultname)
if evt==EVENT_SAVEG3DV4:
defaultname = Blender.sys.makename(Blender.Get("filename"),".g3d",1)
FileSelector(G3DSaverV4, "G3D V4 to save", defaultname)
###########################################################################
# GUI Screenmask as a nice looking feature i Center the Mask
###########################################################################
def gui():
global EVENT_LOADG3D,EVENT_SAVEG3DV3,EVENT_SAVEG3DV4,EVENT_EXIT
xmiddle= GetScreenSize()[0] / 2 #calculate horizontal middle of the screen
#Clearing the Screen all here needs imported BGL + DRAW
glClearColor(0.0, 0.0, 0.0,1)
glClear(GL_COLOR_BUFFER_BIT)
# logo
homedir = Blender.Get("homedir")
Logo = Load(homedir+ os.sep + "scripts" + os.sep + "g3d_logo.png") # Load Logo file
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
Image(Logo,(xmiddle-(Logo.getSize()[0]/2)),20)
glDisable(GL_BLEND)
#Title
tbuffer= "G3D Import Wizard supporting V3 / V4 models"
xpos = xmiddle - (GetStringWidth(tbuffer) /2)
glColor3f(1, 0, 0)
glRasterPos2d(xpos, 70)
Text(tbuffer)
tbuffer= "Please view any Errors / Info in the Blender Console"
xpos = xmiddle - (GetStringWidth(tbuffer) /2)
glColor3f(1, 1, 0)
glRasterPos2d(xpos, 55)
Text(tbuffer)
#Buttons
Button("Load G3D",EVENT_LOADG3D , xmiddle-140, 10, 100, 40)
#Button("Save G3D V3",EVENT_SAVEG3DV3 , xmiddle-100, 10, 100, 40)
#Button("Save G3D V4",EVENT_SAVEG3DV4 , xmiddle, 10, 100, 40)
Button("Exit",EVENT_EXIT , xmiddle+40, 10, 100, 40)
###########################################################################
# Registering GUI events (Activating GUI) THE END
###########################################################################
Register(gui, event, button_event)

View File

@ -0,0 +1,535 @@
#!BPY
# coding: utf-8
"""
Name: 'G3D XML Exporter Ver:1.1'
Blender: 2.43
Group: 'Export'
Tooltip: 'Exports directly to G3D(if xml2g is installed next to this script) or XML (which can be converted to .g3d using xml2g)'
"""
###########################################################################
# 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: 1.0 (Jan 30 2009)
# 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.
# if you want direct .g3d support unpack all needed glexemel files to this directory too:
# LINUX:
# In case of linux its only: xml2g and g3d.dtd ( included is a binary for linux32 bit).
# The sourcecode can be found here:
# http://www.glest.org/files/contrib/tools/glexemel-0.1a.zip
# WINDOWS:
# If you are using windows, all files from the "bin" directory from the
# following zip files are needed:
# http://www.glest.org/files/contrib/tools/glexemel_win32_0.1a.zip
#
# 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.
#
# !!!!!!!!!!!!!!
# !!!new since 0.1.4: !!!
# If you have a non animating model, you have to set the start frame
# and the end frame to '1' in blenders 'timeline'-window. Otherwise a non
# animating animation will be exported, containing a lot of frames and your
# model gets really big.
# !!!!!!!!!!!!!!
#
# 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 :
# You can export all animations made with blenders armature system.
# You have to set the start frame and the end frame in the 'timeline'-window
# to define the animation which will be exported.
# If you animate without using the armatures it will not work!!
#
# LINKS :
# Animating with armatures
# http://www.blender.org/documentation/htmlI/x1829.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
# The following is no longer needed if you copied g3d.dtd and xml2g(.exe) next
# to this script:
#
# 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 :
# 1.1: by Titus Tscharntke (titi)
# fixed bug in windows ( problems with file sparator char )
# 1.0: Big thanks go to Frank Tetzel (Yggdrasil) who finally made a
# direct export to .g3d possible. If you copy g3d.dtd and
# xml2g(.exe) next to this script it will export to .g3d.
# If not, it will export to .xml like before.
#
# 0.1.7 : by Titus Tscharntke (titi)
# removed non 7-bit ascii characters for newer versions of python
# 0.1.6 : by justWeedy(weedkiller)
# Basic colorsettings for maps are often unused but may cause
# models look "bright".
# There are Hardcoded defauld values to avoid this.
# Turn On/Off due the boolean <useHardCodedColor>
# 0.1.5 : by weedkiller
# You don't have to apply scale and rotation anymore
# 0.1.4 : by weedkiller and Titus Tscharntke ( www.titusgames.de )
# New implementation of the animation export ( including some usage changes!!! )
# Exporting animations is now possible with newer Blender versions ( tested with 2.43 )
#
# 0.1.3 : by Titus Tscharntke ( www.titusgames.de )
# Fixed a bug with getProperty() in blender 2.43
#
# 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
import subprocess, os
# part Hardcoded Color >>
HardCodedDiffuse=0.588235
HardCodedSpecular=0.900000
useHardCodedColor='true' #'false' if not wanted
# part Hardcoded Color <<
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
# this will return the properties data if it exists, else the given defaultvalue
def getPropertyIfExists(blenderobject,propertyName,defaultvalue):
propertiesList = blenderobject.getAllProperties()
lpropertiesList = range( 0 , len(propertiesList) )
for iprop in lpropertiesList:
objectProperty = propertiesList[iprop]
currentPropertyName=objectProperty.getName()
if currentPropertyName == propertyName:
return objectProperty.getData()
return defaultvalue
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]
# part Hardcoded Color >>
if useHardCodedColor :
diffuseColor[0] = HardCodedDiffuse
diffuseColor[1] = HardCodedDiffuse
diffuseColor[2] = HardCodedDiffuse
# part Hardcoded Color <<
if material.getTextures()[0]:
image = material.getTextures()[0].tex.getImage()
if image:
textureName = image.getFilename()
textureName = textureName.split(os.path.normcase('/'))[-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
customColor = getPropertyIfExists(object,'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
#frameCount = getPropertyIfExists(object,'frameCount',frameCount)
startFrame = Blender.Get('staframe')
endFrame = Blender.Get('endframe')
frameCount = endFrame-startFrame+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="9.999999" \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 # part Hardcoded Color: THIS WAS ALREADY HARDCODED ...
out.write( ' <Specular><Color r="%f" g="%f" b="%f" /></Specular>\n' % (HardCodedSpecular, HardCodedSpecular, HardCodedSpecular) )
#TEXTURE
if textureName == '':
out.write( ' <!-- NO TEXTURE -->\n' )
else:
out.write( ' <Texture name="%s" />\n' % (textureName) )
#For each FRAME
#VERTICES
framelist={}
l = range( startFrame , startFrame+frameCount )
for frame in l:
# set the right frame
Blender.Set('curframe', frame)
curObject=object
curMatrix= curObject.getMatrix('worldspace')
fmesh = curObject.getData()
# after creation of new Object, Selection is no longer reliable (new obj. are selected, too), already corrected in code (see: curObject= ...)
defmesh=Blender.Mesh.New(curObject.name+'F'+str(frame))
defmesh.getFromObject(curObject.name)
framelist[frame]=defmesh
# just to avoid work
fmesh=defmesh
out.write( ' <Vertices frame="%i">\n' % ( frame-startFrame ) )
# "real" vertex
for vert in fmesh.verts:
vert.co= vert.co * curMatrix
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( startFrame , startFrame+frameCount )
for frame in l:
Blender.Set('curframe', frame)
# getting the created meshes
fmesh = framelist[frame] #.getData()
out.write( ' <Normals frame="%i">\n' % ( frame-startFrame ) )
# "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..." )
Blender.Draw.PupMenu("export warning:%t|WARNING : some faces have been ignored (not triangle)|" +
" 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):
global cmd, extension, scriptsdir
if not filename.endswith(extension): filename = '%s%s' % (filename, extension)
if bsys.exists(filename):
if Blender.Draw.PupMenu('OVERWRITE?%t|File exists') != 1:
return
if cmd:
tmp = bsys.join(scriptsdir, "tmp.xml")
write_obj(tmp)
print "running glexemel"
cmd = [cmd, tmp, filename]
print cmd
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = p.stdout.read()
err = p.stderr.read()
res = p.wait()
if res != 0 or err:
s = ''
if out:
s += out.replace('\n', '|')
if err:
s += err.replace('\n', '|')
Blender.Draw.PupMenu('glexemel error: see console%t|' + s)
print out
print err
print res
else:
write_obj(filename)
print "glexemel not found"
scriptsdir = Blender.Get('scriptsdir')
files = os.listdir(scriptsdir)
cmd = ''
extension=".xml"
for fname in files:
if fname.startswith("xml2g"):
cmd = bsys.join(scriptsdir, fname)
extension = ".g3d"
break
OBJS = Blender.Object.GetSelected()
if not OBJS:
Blender.Draw.PupMenu('ERROR: no objects selected')
else:
fname = bsys.makename(ext=extension)
Blender.Window.FileSelector(fs_callback, "Export XML-G3D", fname)
#Blender.Window.FileSelector(write_obj, "Export")

View File

@ -0,0 +1,57 @@
/**
* File: g3dv4.h
*
* Description:
* Data types used by the G3D format.
*
* This file is copied from portions of the Glest project:
* http://www.glest.org
*
* This file may be distributed under the terms of the GNU General Public
* License.
*/
#ifndef G3DV4_HEADER
#define G3DV4_HEADER
// bug fix from wiki
#pragma pack(push, 1)
typedef float float32;
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
#define NAMESIZE 64
struct FileHeader{
uint8 id[3];
uint8 version;
};
struct ModelHeader{
uint16 meshCount;
uint8 type;
};
enum ModelType{
mtMorphMesh
};
struct MeshHeader{
uint8 name[NAMESIZE];
uint32 frameCount;
uint32 vertexCount;
uint32 indexCount;
float32 diffuseColor[3];
float32 specularColor[3];
float32 specularPower;
float32 opacity;
uint32 properties;
uint32 textures;
};
enum MeshPropertyFlag{
mpfTwoSided= 0,
mpfCustomColor= 1
};
#pragma pack(pop)
#endif

View File

@ -0,0 +1,670 @@
/**
* File: xml2g.c
* Written: Jonathan Merritt <jmerritt@warpax.com>
*
* Description:
* Converts G3Dv4 XML files into the binary representation used in the game.
* Requirements:
* libxml2
*
* Copyright (C) Jonathan Merritt 2005.
* This file may be distributed under the terms of the GNU General Public
* License.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "g3dv4.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/**
* Forward function declarations.
*/
int xml2g3d(xmlDocPtr doc, FILE *outfile);
void usage(char *execname);
unsigned int countChildren(xmlNode *n, xmlChar *name);
int processMesh(xmlNode *n, FILE *outfile);
int readColorChild(xmlNode *n, char *childName, float32 *color);
int processVertices(xmlNode *n, FILE *outfile, uint32 vertexCount);
int processNormals(xmlNode *n, FILE *outfile, uint32 vertexCount);
int processTexcoords(xmlNode *n, FILE *outfile, uint32 vertexCount);
int processIndices(xmlNode *n, FILE *outfile, uint32 indexCount);
/**
* Program entry point.
*
* @param argc: Number of arguments passed to the program.
* @param argv: Array of string arguments.
*
* @returns: EXIT_SUCCESS or EXIT_FAILURE depending upon success or failure
* of the conversion to XML.
*/
int main(int argc, char **argv)
{
char *infilename, *outfilename;
FILE *outfile;
int successFlag;
xmlParserCtxtPtr ctxt;
xmlDocPtr doc;
/* parse command line arguments */
if (argc != 3)
{
usage(argv[0]);
return (EXIT_FAILURE);
}
else
{
infilename = argv[1];
outfilename = argv[2];
}
/* attempt to parse the XML file */
LIBXML_TEST_VERSION
ctxt = xmlNewParserCtxt();
if (ctxt == NULL)
{
printf("Failed to allocate XML parser context!\n");
return (EXIT_FAILURE);
}
doc = xmlCtxtReadFile(ctxt, infilename, NULL, XML_PARSE_DTDVALID);
if (doc == NULL)
{
printf("Could not parse XML file \"%s\"!\n", infilename);
xmlFreeParserCtxt(ctxt);
return (EXIT_FAILURE);
}
/* attempt to open the output binary file */
outfile = fopen(outfilename, "wb");
if (outfile == NULL)
{
printf("Could not open file \"%s\" for writing!\n",
outfilename);
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
xmlCleanupParser();
return (EXIT_FAILURE);
}
/* perform the conversion: XML -> binary */
successFlag = xml2g3d(doc, outfile);
/* close the files */
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
xmlCleanupParser();
fclose(outfile);
/* return a success or failure flag */
if (successFlag)
return (EXIT_SUCCESS);
else
return (EXIT_FAILURE);
}
/**
* Prints a "Usage:" string for the program.
*
* @param execname: Executable name of the program.
*/
void usage(char *execname)
{
printf("Usage:\n");
printf(" %s infile.xml outfile.g3d\n", execname);
return;
}
/**
* Performs the conversion from the XML file format to the G3D binary format.
*
* @param doc: XML DOM document for input.
* @param outfile: Binary file, opened as "wb", for output.
*/
int xml2g3d(xmlDocPtr doc, FILE *outfile)
{
struct FileHeader fh;
struct ModelHeader mh;
xmlNode *root_element;
xmlNode *curNode;
xmlChar version[] = "version";
/* fetch the root element and check it */
root_element = xmlDocGetRootElement(doc);
assert(root_element->type == XML_ELEMENT_NODE);
if (strcmp((char*)root_element->name, "G3D") != 0)
{
printf("G3D document not found!\n");
return FALSE;
}
if (strcmp((char*)xmlGetProp(root_element, version), "4") != 0)
{
printf("Only version 4 G3D documents can be handled!\n");
return FALSE;
}
/* write out the file header */
memset(&fh, 0, sizeof(struct FileHeader));
fh.id[0] = 'G'; fh.id[1] = '3'; fh.id[2] = 'D'; fh.version=4;
fwrite(&fh, sizeof(struct FileHeader), 1, outfile);
/* write out the model header */
memset(&mh, 0, sizeof(struct ModelHeader));
mh.meshCount = (uint16)countChildren(root_element, (xmlChar*)"Mesh");
mh.type = 0;
fwrite(&mh, sizeof(struct ModelHeader), 1, outfile);
/* process each mesh in the file */
curNode = root_element->children;
for (; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, "Mesh") == 0)
{
if (processMesh(curNode, outfile) == FALSE)
return FALSE;
}
}
}
return TRUE;
}
/**
* Counts the number of child nodes with the specified name.
*
* @param n: Node to count the children from.
* @param name: Name of the child nodes to count.
*
* @return: The number of child nodes.
*/
unsigned int countChildren(xmlNode *n, xmlChar *name)
{
unsigned int count = 0;
xmlNode *curNode = NULL;
for (curNode = n->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, (char*)name) == 0)
count++;
}
}
return count;
}
/**
* Processes all <Mesh> elements from the XML file.
*
* @param n: <Mesh> element
* @param outfile: Binary file, opened as "wb", for output.
*
* @return: TRUE if successful, FALSE otherwise.
*/
int processMesh(xmlNode *n, FILE *outfile)
{
xmlChar name[] = "name";
xmlChar frameCount[] = "frameCount";
xmlChar vertexCount[] = "vertexCount";
xmlChar indexCount[] = "indexCount";
xmlChar specularPower[] = "specularPower";
xmlChar opacity[] = "opacity";
xmlChar twoSided[] = "twoSided";
xmlChar customColor[] = "customColor";
xmlChar diffuseTexture[] = "diffuseTexture";
float32 color[3];
struct MeshHeader mh;
uint8 texname[NAMESIZE];
int foundFlag = FALSE;
xmlNode *texn = NULL;
xmlNode *curNode = NULL;
/* make sure we're dealing with a <Mesh> element */
assert(strcmp((char*)n->name, "Mesh") == 0);
/* populate the MeshHeader structure appropriately */
memset(&mh, 0, sizeof(struct MeshHeader));
strncpy((char*)mh.name, (char*)xmlGetProp(n, name), NAMESIZE);
mh.frameCount = (uint32)atoi((char*)xmlGetProp(n, frameCount));
mh.vertexCount = (uint32)atoi((char*)xmlGetProp(n, vertexCount));
mh.indexCount = (uint32)atoi((char*)xmlGetProp(n, indexCount));
if (readColorChild(n, "Diffuse", color) == FALSE)
return FALSE;
mh.diffuseColor[0] = color[0];
mh.diffuseColor[1] = color[1];
mh.diffuseColor[2] = color[2];
if (readColorChild(n, "Specular", color) == FALSE)
return FALSE;
mh.specularColor[0] = color[0];
mh.specularColor[1] = color[1];
mh.specularColor[2] = color[2];
mh.specularPower = (float32)atof((char*)xmlGetProp(n, specularPower));
mh.opacity = (float32)atof((char*)xmlGetProp(n, opacity));
mh.properties = 0;
if (strcmp((char*)xmlGetProp(n, twoSided), "true") == 0)
mh.properties += (0x1 << mpfTwoSided);
if (strcmp((char*)xmlGetProp(n, customColor), "true") == 0)
mh.properties += (0x1 << mpfCustomColor);
mh.textures = 0;
if (strcmp((char*)xmlGetProp(n, diffuseTexture), "true") == 0)
mh.textures = 1;
/* write the MeshHeader */
fwrite(&mh, sizeof(struct MeshHeader), 1, outfile);
/* if we have a texture, then also write its name */
foundFlag = FALSE;
if (mh.textures)
{
for (texn=n->children; texn; texn = texn->next)
{
if (texn->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)texn->name, "Texture") == 0)
{
foundFlag = TRUE;
break;
}
}
}
if (foundFlag == FALSE)
{
printf("Could not find <Texture> element!\n");
return FALSE;
}
memset(texname, 0, NAMESIZE);
strncpy((char*)texname,
(char*)xmlGetProp(texn, (xmlChar*)"name"), NAMESIZE);
fwrite(texname, NAMESIZE, 1, outfile);
}
/* write out vertices */
foundFlag = FALSE;
for (curNode=n->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, "Vertices") == 0)
{
foundFlag = TRUE;
if (processVertices(curNode, outfile,
mh.vertexCount) == FALSE)
{
return FALSE;
}
}
}
}
if (foundFlag == FALSE)
{
printf("No <Vertices> found!\n");
return FALSE;
}
/* write out normals */
foundFlag = FALSE;
for (curNode=n->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, "Normals") == 0)
{
foundFlag = TRUE;
if (processNormals(curNode, outfile,
mh.vertexCount) == FALSE)
{
return FALSE;
}
}
}
}
if (foundFlag == FALSE)
{
printf("No <Normals> found!\n");
return FALSE;
}
/* write out texture coordinates */
if (mh.textures)
{
foundFlag = FALSE;
for (curNode=n->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, "TexCoords")
== 0)
{
foundFlag = TRUE;
if (processTexcoords(curNode,
outfile, mh.vertexCount) ==
FALSE)
{
return FALSE;
}
}
}
}
if (foundFlag == FALSE)
{
printf("No <TexCoords> found!\n");
return FALSE;
}
}
/* write out indices */
foundFlag = FALSE;
for (curNode=n->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, "Indices") == 0)
{
foundFlag = TRUE;
if (processIndices(curNode, outfile,
mh.indexCount) == FALSE)
{
return FALSE;
}
}
}
}
if (foundFlag == FALSE)
{
printf("No <Indices> found!\n");
}
return TRUE;
}
/**
* Reads the <Color> child of a child of a node.
*
* For example:
* <Mesh>
* <Diffuse>
* <Color r="0.1" g="0.2" b="0.3"/>
* </Diffuse>
* </Mesh>
* If <Mesh> is passed in as n and childNode is the string "Diffuse", then
* the method will read the <Color> element from within the <Diffuse> element.
*
* @param n: Node from which the children should be read.
* @param childNode: Name of the child element that owns a <Color> element.
* @param color: float32[3] array to hold RGB color.
*
* @return TRUE if the method succeeds, FALSE otherwise.
*/
int readColorChild(xmlNode *n, char *childNode, float32 *color)
{
int foundFlag = FALSE;
double r,g,b;
xmlNode *curNode = NULL;
/* make sure that n is an element */
assert(n->type == XML_ELEMENT_NODE);
/* search for the first child node matching the given name */
for (curNode=n->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, (char*)childNode) == 0)
{
foundFlag = TRUE;
break;
}
}
}
if (foundFlag == FALSE)
{
printf("Could not find child node \"%s\"!\n", childNode);
return FALSE;
}
/* search for a <Color> node child */
for (curNode=curNode->children; curNode; curNode = curNode->next)
{
if (curNode->type == XML_ELEMENT_NODE)
{
if (strcmp((char*)curNode->name, "Color") == 0)
{
r = atof((char*)xmlGetProp(curNode,
(xmlChar*)"r"));
g = atof((char*)xmlGetProp(curNode,
(xmlChar*)"g"));
b = atof((char*)xmlGetProp(curNode,
(xmlChar*)"b"));
color[0] = (float32)r;
color[1] = (float32)g;
color[2] = (float32)b;
return TRUE;
}
}
}
printf("Could not find <Color> child of \"%s\"!\n", childNode);
return FALSE;
}
/**
* Writes vertices into the binary file.
*
* @param n: <Vertices> element.
* @param outfile: Binary file, opened as "wb", for output.
* @param vertexCount: Expected number of vertices to process.
*
* @return: TRUE if the method succeeds, FALSE otherwise.
*/
int processVertices(xmlNode *n, FILE *outfile, uint32 vertexCount)
{
xmlNode *v;
uint32 counted_vertices = 0;
float32 p[3];
/* check we've been passed a <Vertices> element */
assert(n->type == XML_ELEMENT_NODE);
assert(strcmp((char*)n->name, "Vertices") == 0);
/* iterate over all <Vertex> children */
for (v=n->children; v; v=v->next)
{
if ((v->type == XML_ELEMENT_NODE) &&
(strcmp((char*)v->name, "Vertex") == 0))
{
counted_vertices++;
p[0] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"x"));
p[1] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"y"));
p[2] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"z"));
fwrite(p, 3*sizeof(float32), 1, outfile);
}
}
/* check that the correct number of vertices were processed */
if (counted_vertices != vertexCount)
{
printf("Found %d vertices, expected %d!\n",
counted_vertices, vertexCount);
return FALSE;
}
return TRUE;
}
/**
* Writes normals into the binary file.
*
* @param n: <Normals> element.
* @param outfile: Binary file, opened as "wb", for output.
* @param vertexCount: Expected number of normals to process.
*
* @return: TRUE if the method succeeds, FALSE otherwise.
*/
int processNormals(xmlNode *n, FILE *outfile, uint32 vertexCount)
{
xmlNode *v;
uint32 counted_normals = 0;
float32 p[3];
/* check we've been passed a <Normals> element */
assert(n->type == XML_ELEMENT_NODE);
assert(strcmp((char*)n->name, "Normals") == 0);
/* iterate over all <Normal> elements */
for (v=n->children; v; v=v->next)
{
if ((v->type == XML_ELEMENT_NODE) &&
(strcmp((char*)v->name, "Normal") == 0))
{
counted_normals++;
p[0] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"x"));
p[1] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"y"));
p[2] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"z"));
fwrite(p, 3*sizeof(float32), 1, outfile);
}
}
/* check that the correct number of normals were processed */
if (counted_normals != vertexCount)
{
printf("Found %d normals, expected %d!\n",
counted_normals, vertexCount);
return FALSE;
}
return TRUE;
}
/**
* Writes texture coordinates into the binary file.
*
* @param n: <TexCoords> element.
* @param outfile: Binary file, opened as "wb", for output.
* @param vertexCount: Expected number of vertices to process.
*
* @return: TRUE if the method succeeds, FALSE otherwise.
*/
int processTexcoords(xmlNode *n, FILE *outfile, uint32 vertexCount)
{
xmlNode *v;
uint32 counted_texco = 0;
float32 p[2];
/* check we've been passed a <TexCoords> element */
assert(n->type == XML_ELEMENT_NODE);
assert(strcmp((char*)n->name, "TexCoords") == 0);
/* iterate over all <ST> children */
for (v=n->children; v; v=v->next)
{
if ((v->type == XML_ELEMENT_NODE) &&
(strcmp((char*)v->name, "ST") == 0))
{
++counted_texco;
p[0] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"s"));
p[1] = (float32)atof((char*)xmlGetProp(v,
(xmlChar*)"t"));
fwrite(p, 2*sizeof(float32), 1, outfile);
}
}
/* check that the correct number of texco were processed */
if (counted_texco != vertexCount)
{
printf("Found %d texture coordinates, expected %d!\n",
counted_texco, vertexCount);
return FALSE;
}
return TRUE;
}
/**
* Writes indices into the binary file.
*
* @param n: <Indices> element.
* @param outfile: Binary file, opened as "wb", for output.
* @param indexCount: Expected number of indices to process.
*
* @return: TRUE if the method succeeds, FALSE otherwise.
*/
int processIndices(xmlNode *n, FILE *outfile, uint32 indexCount)
{
xmlNode *v;
uint32 counted_indices = 0;
uint32 index;
/* check we've been passed an <Indices> element */
assert(n->type == XML_ELEMENT_NODE);
assert(strcmp((char*)n->name, "Indices") == 0);
/* iterate over all <Ix> children */
for (v=n->children; v; v=v->next)
{
if ((v->type == XML_ELEMENT_NODE) &&
(strcmp((char*)v->name, "Ix") == 0))
{
++counted_indices;
index = (uint32)atoi((char*)xmlGetProp(v,
(xmlChar*)"i"));
fwrite(&index, 1*sizeof(uint32), 1, outfile);
}
}
/* check that the correct number of indices were processed */
if (counted_indices != indexCount)
{
printf("Found %d indices, expected %d!\n",
counted_indices, indexCount);
return FALSE;
}
return TRUE;
}