"""
Name: 'IGTL TED (.ted)'
Blender: 249.2
Group: 'Export'
Tooltip: 'Export a TED resource file from selection (DESTRUCTIVE)'
"""
__author__ = "ImaginaryZ"
__url__ = ("blender", "gocaco")
__version__ = "2010-06-04"
__bpydoc__ = """\
Exports all selected data to a TED Resource File (Text Extracted Data)
#ERROR - does not attempt to fix object parenting;
if you have a armature parent, it's all wrong and the data has to be re-adjusted.
***WARNING***
Exporter is destructive (?); Save your file before exporting, then reload it to prevent damage! I.E. never save AFTER exporting.
Progress:
Armatures - Mostly complete; definitions of spline points iffy; Needs revision
Channels - Mostly complete; Only uses Actions; need to create IPO channels too for object animations
Animations - Mostly complete; Only uses Actions; need to use IPO channels too for object animations
Shapes - None; Needs work from game properties.
Meshes - Mostly complete; Includes matrix palette splitting; Missing stripize; Missing optomization, no meshkeyframes or controllers.
Textures - Mostly complete; Dumps texture data and filenames; needs LZSS/other byte compressions on texture data. (Hex->ASCII works great)
Materials - Started; Exports basic material properties; needs shaders and texlayer effects, and additional parameters
Models - N/A
Scenes - Mostly complete; Exports all objects in all scenes; Exports properties and world positions/orientations (may not be correctly oriented)
Objects - Mostly complete; missing some data from objects
Conversion - Partially Added; Testing, and in progress of this. Probably *the* most important feature.
Regression - XML writer throws exceptions and creates a DTD for debugging. Works so far; data validation is not in place yet.
Description:
A TED file is a specially formatted XML document.
It has different rules than XML and is far more strict. Adhering to these rules makes the file stable.
tag names CANNOT contain whitespace < tag > is not allowed, <tag > nope, <tag> correct, <this rules> nope
the characters '<' '>' '&' may not appear anywhere in the file; unless they are explicitly used as tags.
whitespace is ignored, but is used to define spacing between data elements
Example: 0.0 0.0 0.0 is 3 values.
String data includes leading and trailing whitespace in certain tags; it MUST be enclosed by special tags.
Example: <name>Whooopee! Yes!</name> is NOT the same as: <name> Whooopee! Yes! </name>
Numerical types are strictly formatted:
Integer can only contain: 0123456789- Examples: 16 -389 0
Float can only contain: 0123456789-. Examples: 0.0 -1.5632 764590823.5678
Anything else is a STRING; and MUST be enclosed by special tags. (Strings must be tag-enclosed as they include whitespace)
Tag depth is highly meaningful. It is used to create the loader and state machine.
If you know your depth in a TED file, you know exactly what to expect when you read a file.
Tag data specification is highly rigid;
A single tag can only contain 1 type of data, or other tags.
so, a tag can have a list of floats, a list of strings, a list of other tags, but NOT a mix.
A tag can have other tags in it, but those MUST be all tags. No linear data is in these categorical tags.
Tags that specify arrays of primitive data all have a <count> and <data> tag, which obviously have the data needed.
some tags specify a array of other tags; this is fine and does not need the <data> encapsulation. (because this allows tags to store tags, which is OK)
Tabbing and whitespace is not required, but is there for lexical description. Most XML readers can do this automatically for you.
Raw binary data can be stored in a TED file, but is only storabe in <hexdata> tags; Which are Hex->ASCII saved, as defined by a translation table.
You will notice the exporter ALSO generates the C-Code to define the exact format specification for the file, as well as C-Code for ENUM's for your loading routine.
I Believe TED files can be loaded by any XML loader as well; But this will have to be proven later.
#Texcoords = t
#Normals = n
#Tangents = T
#Colors = c
#Weights = w
#Coords = v
<!-- This is acceptable, because <weight> is a tag, not data. However, this is not allowed because 'weightset' is an array. -->
<weightset>
<count>2</count>
<weight>...</weight>
<weight>...</weight>
<!-- This is the corrected form; proporly encapsulating arrays of data with tags that contain counts. no data is mixed. -->
<weightset>
<count>2</count>
<data>
<weight>...</weight>
<weight>...</weight>
</data>
<!-- This is NOT acceptable, because there are tags and raw data mixing. -->
<verticies>
<count>2</count>
0.0 -16.0 12.0
<!-- This is NOT acceptable, because there is a tag, and THEN a fixed data specification. -->
<weight>
<bone>Name</bone>
0.75 1
</weight>
<!-- This is NOT acceptable, because there are multiple data types inside of data -->
<weight>
<bone>Name</bone>
<data>0.75 1</data>
</weight>
<!-- This is acceptable, because all data of differing types is encapsulated; and no datatypes are mixed. -->
<weight>
<bone>Name</bone>
<index>1</index>
<value>0.75</value>
</weight>
"""
import Blender
from Blender import Mathutils
import math
import operator
######################################################################
#
#Define the actual DTD for the file format; This is loaded and can be converted to C-code optionally (as a debugging tool)
#
#Note that a "true" DTD has a FIXED order; the DTD for a TED file looks like this:
#"string" "uint" "int" "float" are valid types.
#any unsigned integer is valid as a count. Leave blank for infinite or user defined length.
#
const_DTD_ted = """\
ted
author string
checksum string
datagroup string
date string
name string
armatures
count uint 1
armature
name string
bones
count uint 1
bone
depth uint 1
length float 1
matrix float 9
quaternion float 4
name string
parent string
position float 3
radius_end float 1
radius_start float 1
scalemode uint 1
ik_chains
count uint 1
ik_chain
bone string
depth uint 1
name string
rotation float 9
position float 9
splinepoints
count uint 1
splinepoint
bone string
direction float 3
name string
position float 3
channels
count uint 1
channel
name string
target string
timelength float 1
timestart float 1
keypositions
count uint 1
keytimes float
positions float
keyquaternions
count uint 1
keytimes float
quaternions float
keymatrices
count uint 1
keytimes float
matrices float
keyscales
count uint 1
keytimes float
scales float
animations
count uint 1
animation
count uint 1
name string
timelength float 1
timescale float 1
timestart float 1
blendchannel
name string
timelength float 1
timescale float 1
timestart float 1
count uint 1
times float
weights float
meshes
count uint 1
mesh
name string
colors
count uint 1
data float
coords
count uint 1
data float
normals
count uint 1
data float
polygroups
count uint 1
polygroup
count uint 1
data uint
format string
name string
vabegin uint
vaend uint
matrixlinks
count uint 1
matrixlink
armature string
index uint 1
bone uint 1
name string
stringmap
count uint 1
link
data string
name string
tangents
count uint 1
data float
texcoords
count uint 1
data float
vertexarray
format string
count uint 1
data uint
vertgroups
count uint 1
vertgroup
name string
count uint
indicies uint
weights float
weightsets
count uint 1
weightset
count uint 1
bones
name string
indicies uint
weights float
textures
count uint 1
texture
bpp uint 1
height uint 1
hexdata
size uint 1
table string
compression string
count uint 1
data string
name string
type string
width uint 1
materials
count uint 1
material
ambient float 4
diffuse float 4
emissive float 4
name string
specular float 4
transparency float 1
textures
count uint 1
texture
name string
blend uint
mode string
scenes
count uint 1
scene
name string
objects
count uint 1
object
depth uint 1
matrix float 9
name string
parent string
position float 3
scale float 3
values
count uint 1
value
data string
name string
type string
links
count uint 1
link
type string
name string
""";
def convert_DTD_to_ENUM( S ):
EnumTree = [];
line_stack = [];
line_index = 0;
line = "";
depth_prev = 0;
#Read chars in S until '\n';
for c in S:
if( c == '\n' ):
#in that line, determine tab depth (==node depth)
#If depth is NOT +1 or -n; formatting error.
depth = 0;
r_index = 0;
for r in line:
if( r == '\t' ):
depth += 1;
r_index += 1;
else:
break;
#name type count
name = "";
for r in line[r_index:]:
r_index += 1;
if( r == ' ' ):
break;
else:
name += r;
stype = "";
for r in line[r_index:]:
r_index += 1;
if( r == ' ' ):
break;
else:
stype += r;
scount = "";
for r in line[r_index:]:
r_index += 1;
if( r == ' ' ):
break;
else:
scount += r;
vcount = 0;
if( scount.isdigit() ):
vcount = int( scount );
else:
vcount = scount; #Referencing previous node as count of array.
#Make sure we have the space
while( depth >= len( line_stack ) ):
line_stack.append( [] );
#while( depth < len( line_stack ) ):
# line_stack.pop()
#Check for logical consistency
if( depth > (depth_prev + 1) ):
print "#ERROR, DTD has depth issue at element:" + name + " " + str(depth) + " " + str(depth_prev) + " " +str( line_stack );
return None;
depth_prev = depth;
#add to stack
line_stack[ depth ] = [name, stype, vcount]; #"push"
#convert to enumeration string
Estring = name;
while( depth > 0 ):
depth -= 1;
Estring = line_stack[ depth ][0] +"_"+Estring;
Estring = Estring.upper();
enumindex = len(EnumTree) + 1;
EnumTree.append( [ Estring, enumindex ] );
line = "";
else:
line += c;
return EnumTree;
######################################################################
#
#Class definitions
#
#--------------------------------------------------------------------
#
#Write a DXML xml file easier; also magically builds a DTD tree for you (kickass!)
#
class dxmlwriter:
class dxmldtd:
def __init__(self):
self.index = 0; #Internal index of this tag
self.tagname = ""; #Name of this tag
self.depth = 0; #Depth this tag exists at
self.taglist = []; #List of all tags before this one that define this tag exactly. (stack)
self.datatype = 0; #The data type of this tag (usually 'NONE' for other tags, sometimes float, int, ect...)
self.is_array = 0; #If this tag is an array, we expect a <count> and <data> for this tag, where <data> is filled with datatype for count.
self.is_fixed_array = 1;
self.element_count = 0; #Number of elements written (if data)
def __init__(self):
self.finaldata = "";
self.data = "";
self.tagdict = {};
self.whitetabs = [];
self.depth = 0;
self.tagstack = [];
self.precision = 6;
self.precisionstring = "%#."+str( self.precision )+"f";
self.autoDTD = {}; #Builds a string tree for an automatic DTD; autodetects arrays of types, and problems with exporting.
#self.autoDTD_curr_tagstack = [];
self.autoDTD_count = 0;
self.autoDTD_last_key = None;
self.autoDTD_last_typearray = 0;
self.autoDTD_last_typemask = 0;
self.autoDTD_last_count = 0;
self.MASK_STRING = 128;
self.MASK_FLOAT = 16;
self.MASK_INT = 2;
self.MASK_NONE = 0;
i = 0;
curstr = "";
while( i < 63 ):
self.whitetabs.append( curstr );
curstr += "\t";
i += 1;
#
#Returns true if current typemask is of a single type
#
def _autoDTD_singletype( self ):
if self.autoDTD_last_typemask == self.MASK_STRING:
return True;
elif self.autoDTD_last_typemask == self.MASK_FLOAT:
return True;
elif self.autoDTD_last_typemask == self.MASK_INT:
return True;
elif self.autoDTD_last_typemask == self.MASK_NONE:
return True;
else:
#Python screws this up a lot.
if self.autoDTD_last_typemask == (self.MASK_INT | self.MASK_FLOAT):
self.autoDTD_last_typemask = self.MASK_FLOAT;
return False;
def _autoDTD_create( self, tag ):
#
#AUTO DTD begin
tstr = "";
for t in self.tagstack:
tstr += "<"+t+">";
tstr += "<"+tag+">";
self.autoDTD_last_key = tstr;
if tstr in self.autoDTD:
#Take largest count
if( self.autoDTD[ tstr ].element_count_prev == 0 ):
self.autoDTD[ tstr ].element_count_prev = self.autoDTD[ tstr ].element_count;
elif( self.autoDTD[ tstr ].element_count_prev < self.autoDTD[ tstr ].element_count ):
self.autoDTD[ tstr ].element_count_prev = self.autoDTD[ tstr ].element_count;
self.autoDTD[ tstr ].is_fixed_array = 0;
elif( self.autoDTD[ tstr ].element_count_prev == self.autoDTD[ tstr ].element_count ):
#self.is_fixed_array = 1;
pass;
self.autoDTD[ tstr ].element_count = 0;
else:
NDTD = dxmlwriter.dxmldtd();
NDTD.index = self.autoDTD_count;
self.autoDTD_count += 1;
NDTD.tagname = tag; #Name of this tag
NDTD.depth = self.depth; #Depth this tag exists at
NDTD.taglist = [ v for v in self.tagstack ];
NDTD.datatype = self.MASK_NONE; #The data type of this tag (usually 'NONE' for other tags, sometimes float, int, ect...)
NDTD.is_array = 0; #If this tag is an array, we expect a <count> and <data> for this tag, where <data> is filled with datatype for count.
NDTD.is_fixed_array = 1;
NDTD.element_count = 0;#self.autoDTD_last_count;
NDTD.element_count_prev = 0;#self.autoDTD_last_count;
self.autoDTD[ tstr ] = NDTD;
#AUTO DTD end
#
#
#Commit current writing of data to the final stored data (far more efficient in memory)
#
def commit(self):
self.finaldata += self.data;
self.data = "";
#
#Automatically writes out values to a string correctly; don;t use nested lists if you can avoid it; don't mix data types.
#
def _writevalue( self, value ):
tv = type( value );
tname = tv.__name__;
if tname == 'float':
#Use precision floats?
S = (self.precisionstring) % value;
#
#remove 'e' floats too?? #ERROR
sindex = S.find('.') + self.precision + 1; #Truncate to desired decimal place
S = S[:sindex].rstrip('0'); #Assign and clean
if( S[-1] == '.' ): #error check ("0." must be "0.0")
S += '0';
self.autoDTD_last_typemask |= self.MASK_FLOAT;
self.autoDTD_last_count += 1;
return S;
elif tname == 'list':
self.autoDTD_last_typearray = 1;
first = 1;
ss = "";
for a in value:
if( not first ):
ss += " ";
ss += self._writevalue( a );
first = 0;
return ss;
elif tname == 'int':
self.autoDTD_last_typemask |= self.MASK_INT;
self.autoDTD_last_count += 1;
return str(value);
else:
self.autoDTD_last_typemask |= self.MASK_STRING;
self.autoDTD_last_count += 1;
return str(value);
#
#Push a tag onto the stack
#
def push( self, tag ):
#self.tagdict[ tag ] = self.data.length
self.data += (self.whitetabs[ self.depth ] + "<" + tag + ">\n");
self._autoDTD_create( tag );
self.depth += 1;
self.tagstack.append( tag );
#
#Write a tag onto a single line; used for things that are small like vectors and points.
#
def writeline( self, tag, udata ):
self.autoDTD_last_typearray = 0;
self.autoDTD_last_typemask = self.MASK_NONE;
self.autoDTD_last_count = 0;
self._autoDTD_create( tag );
net = (self.whitetabs[ self.depth ] + "<" + tag + ">");
#if( type(udata) == '< type='array'>' ):
net += self._writevalue( udata );
net += "</" + tag + ">\n";
self.data += net;
#
#AUTO DTD begin
if self.autoDTD_last_key != None:
self.autoDTD[ self.autoDTD_last_key ].datatype |= self.autoDTD_last_typemask;
self.autoDTD[ self.autoDTD_last_key ].is_array |= self.autoDTD_last_typearray;
self.autoDTD[ self.autoDTD_last_key ].element_count = self.autoDTD_last_count;
if( self._autoDTD_singletype() ):
#Current tag line has a single unified type; so it passes and is stored in the DTD as needed.
pass;
else:
raise Exception("Current tag has multiple types: " + str( self.tagstack ) );
self.autoDTD_last_count = 0;
#AUTO DTD end
#
#
#Specialized function to write-replace data in the file (IE for checksums); Don;t use.
#
def writereplace( self, tag, udata, cdepth, begin, end ):
S = str((self.whitetabs[ cdepth ] + "<" + tag + ">"));
#if( type(udata) == '< type='array'>' ):
S += self._writevalue( udata );
S += "</" + tag + ">\n";
S0 = self.finaldata[0:begin];
self.finaldata = S0 + S + self.finaldata[end:];
#i = begin;
#i_end = end;
#s = 0;
#s_end = len(S);
#if( (end - begin) < s_end ):
# s_end = (end - begin);
#while( s < s_end ):
# self.data[i] = S[s];
# s += 1;
# i += 1;
#
#Write a tag onto a single line; used for things that are small like vectors and points.
#
def writedataline( self, udata ):
self.autoDTD_last_typearray = 1;
self.autoDTD_last_typemask = self.MASK_NONE;
self.autoDTD_last_count = 0;
self.data += self.whitetabs[ self.depth ] + self._writevalue( udata ) + "\n";
#
#AUTO DTD ?
if self.autoDTD_last_key != None:
self.autoDTD[ self.autoDTD_last_key ].datatype |= self.autoDTD_last_typemask;
self.autoDTD[ self.autoDTD_last_key ].is_array |= self.autoDTD_last_typearray;
self.autoDTD[ self.autoDTD_last_key ].element_count += self.autoDTD_last_count;
#AUTO DTD end
#
#
#Write a single comment line into the file (not used much)
#
def comment( self, udata ):
self.data += (self.whitetabs[ self.depth ] + "<!-- " + udata + " -->\n");
#
#Write a array of contiguous data, splitting by tabs every division values. This is a formatting routine, really.
#
def writearraydata( self, udata, division ):
self.autoDTD_last_typearray = 1;
self.autoDTD_last_typemask = self.MASK_NONE;
self.autoDTD_last_count = 0;
net = "";
imax = len(udata);
i = 0;
while( i < imax ):
line = "";
jmax = i + division;
if( jmax > imax ):
jmax = imax;
first = 1;
while( i < jmax ):
if( not first ):
line += " ";
line += self._writevalue(udata[i]);
i += 1;
first = 0;
net += self.whitetabs[ self.depth ] + line + "\n"; #oiffset depth?
self.data += net;
#
#AUTO DTD begin
if( self._autoDTD_singletype() ):
#Current tag line has a single unified type; so it passes and is stored in the DTD as needed.
pass;
else:
raise Exception("Current array tag has multiple types: " + str( self.tagstack ) );
if self.autoDTD_last_key != None:
self.autoDTD[ self.autoDTD_last_key ].datatype |= self.autoDTD_last_typemask;
self.autoDTD[ self.autoDTD_last_key ].is_array |= self.autoDTD_last_typearray;
self.autoDTD[ self.autoDTD_last_key ].element_count += self.autoDTD_last_count;
#AUTO DTD end
#
#
#Pop a tag off the stack; optionally throw an exception if a unexpected tag is popped.
#
def pop( self, tagcheck = None ):
#
#AUTO DTD begin
#if( self._autoDTD_singletype() ):
# #Current tag line has a single unified type; so it passes and is stored in the DTD as needed.
# pass;
#else:
# raise Exception("Current array has multiple types: " + str( self.tagstack ) );
#if self.autoDTD_last_key != None:
# self.autoDTD[ self.autoDTD_last_key ].datatype |= self.autoDTD_last_typemask;
# self.autoDTD[ self.autoDTD_last_key ].is_array |= self.autoDTD_last_typearray;
#AUTO DTD end
#
#self.tagdict[ tag ] = self.data.length
self.depth -= 1;
if( self.depth < 0 ):
raise Exception("Popped past end of stack!");
self.depth = 0;
if( tagcheck != None ):
if( self.tagstack[-1] != tagcheck ):
raise Exception("Mispopped stack: " + str( self.tagstack ) );
self.data += (self.whitetabs[ self.depth ] + "</" + self.tagstack[-1] + ">\n");
self.tagstack.pop();
#
#AUTO DTD begin
#if self.autoDTD_last_key != None:
# self.autoDTD[ self.autoDTD_last_key ].element_count = self.autoDTD_last_count;
# self.autoDTD_last_count = 0;
#
#
#
#Set precision when using float conversion
#
def setprecision( self, value ):
self.precision = value;
self.precisionstring = "%#."+str( self.precision )+"f";
def getdata( self ): #Return current uncommitted file data
return self.data;
def getfinaldata( self ): #Return committed file data
return self.finaldata;
def getdepth( self ): #Get the current depth of the tag stack
return self.depth;
def getprecision( self, value ): #Get the precision currently se
return self.precision;
def getsize( self ): #Get current total filesize
return len( self.finaldata + self.data );
def getdtdstring( self ):
#
#Convert tags to data & relative string tag index (for making string indexed binary files)
#
depthmakers = [];
depthmaxes = [];
sortkeys = [];
for dtdkey in self.autoDTD:
dtdelem = self.autoDTD[ dtdkey ];
alltags = [ n for n in dtdelem.taglist ];
alltags.append( dtdelem.tagname );
tagvi = [];
t_index = 0;
for taggy in alltags:
#Convert string tree into index tree (string->index map)
while( len( depthmakers ) <= t_index ):
depthmakers.append( {} );
depthmaxes.append( 0 );
tagdex = -1;
if( taggy in depthmakers[ t_index ] ):
tagdex = depthmakers[ t_index ][ taggy ];
else:
tagdex = depthmaxes[ t_index ];
depthmakers[ t_index ][ taggy ] = tagdex;
depthmaxes[ t_index ] += 1;
tagvi.append( tagdex );
#tagvi.append( taggy );
t_index += 1;
#Convert tagvi to special sorting method ?
sortkeys.append( [ tagvi, dtdelem ] );
#Tree sorting -> Correctify
def SortBySpecial( A, B ):
xoA = A[0];
xoB = B[0];
xoA_len = len( A[0] );
xoB_len = len( B[0] );
lex = xoA_len;
if( xoB_len < lex ):
lex = xoB_len;
i = 0;
while( i < lex ):
#
if( xoA[i] > xoB[i] ):
return 1;
elif( xoA[i] < xoB[i] ):
return -1;
else:
pass;
#
i += 1;
#
# ??
#
if( xoA_len > xoB_len ):
return 1;
elif( xoA_len == xoB_len ):
return 0;
else:
return -1;
sortkeys.sort( SortBySpecial );
#
#by doing this, you can automatically generate a binary DXML file; since you have a string + depth tree;
#It's trivial to create a number <--> string conversion that represents this file in a BRF style format
#
dtdstring = "<dtd>\n";
dtdstring += "\t<count>" + str(len(sortkeys)) + "</count>\n";
for dtddata in sortkeys:
dtdelem = dtddata[1];
taglist = "";
first = 1;
for tl in dtdelem.taglist:
if not first:
taglist += " ";
first = 0;
taglist += tl;
strtype = "none";
if( dtdelem.datatype == self.MASK_STRING ):
strtype = "string";
elif( dtdelem.datatype == self.MASK_FLOAT ):
strtype = "float";
elif( dtdelem.datatype == self.MASK_INT ):
strtype = "int";
#"/"+str(dtdelem.is_array) +
linestr = self.whitetabs[ 1 + dtdelem.depth ] + "<tag>" + str(dtdelem.depth) + ":" + str(dtdelem.element_count) + "/" + str(dtdelem.is_fixed_array) + ":" + strtype + ":" + taglist + " " + dtdelem.tagname + "</tag>";
linestr += "\n"
dtdstring += linestr;
dtdstring += "</dtd>\n";
#Can even write some C++ code out of this (?)
#
#
#
return dtdstring;
#
#Stores an armature
#
class cArmature:
class cBone:
def __init__(self):
self.index = -1;
self.name = "";
self.parent = "";
self.parent_index = -1;
self.pos = [0,0,0];
self.matrix = [1,0,0,0,1,0,0,0,1];
self.scalemode = 0;
self.radius_start = 0.0;
self.radius_end = 0.0;
self.length = 0;
self.depth = 0;
self.child_count = 0;
class cIKChain:
def __init__(self):
self.index = -1;
self.name = "";
self.startbone = "";
self.endbone = "";
self.depth = 0;
self.method = 0;
self.iklim_pos_min = [0,0,0];
self.iklim_pos_max = [0,0,0];
self.iklim_pos_stiff = [0,0,0];
self.iklim_rot_min = [0,0,0];
self.iklim_rot_max = [0,0,0];
self.iklim_rot_stiff = [0,0,0];
def copy(self, old):
self.index = old.index;
self.name = old.name;
self.startbone = old.startbone;
self.endbone = old.endbone;
self.depth = old.depth;
self.method = old.method;
self.iklim_pos_min = [ v for v in old.iklim_pos_min ];
self.iklim_pos_max = [ v for v in old.iklim_pos_max ];
self.iklim_pos_stiff = [ v for v in old.iklim_pos_stiff ];
self.iklim_rot_min = [ v for v in old.iklim_rot_min ];
self.iklim_rot_max = [ v for v in old.iklim_rot_max ];
self.iklim_rot_stiff = [ v for v in old.iklim_rot_stiff ];
class cSplinePoint:
def __init__(self):
self.index = -1;
self.name = "";
self.bone = "";
self.x = 0;
self.y = 0;
self.z = 0;
self.vx = 0;
self.vy = 0;
self.vz = 0;
class cPossibleAnimation:
def __init__(self):
self.index = -1;
self.name = "";
def __init__(self):
self.index = -1;
self.name = "";
self.bones = []; #array of gBones
self.ikchains = []; #Array of cIKChain
self.splinepoints = []; #Array of cSplinePoint
self.possibleanimations = []; #Array of strings
self._map_bone_name_to_index = {}; #Internal only
#
#Stores an animation channel for either an object or a bone
#
class cChannel:
class cKPosition:
def __init__(self):
self.index = -1;
self.time = 0.0;
self.x = 0.0;
self.y = 0.0;
self.z = 0.0;
class cKMatrix:
def __init__(self):
self.index = -1;
self.time = 0.0;
self.matrix = [1,0,0,0,1,0,0,0,1];
class cKQuat:
def __init__(self):
self.index = -1;
self.time = 0.0;
self.x = 0.0;
self.y = 0.0;
self.z = 0.0;
self.w = 1.0;
class cKScale:
def __init__(self):
self.index = -1;
self.time = 0.0;
self.x = 0.0;
self.y = 0.0;
self.z = 0.0;
def __init__(self):
self.index = -1;
self.name = "";
self.target = "";
self.time_start = 0.0;
self.time_end = 0.0;
self.key_positions = [];
self.key_quats = [];
self.key_matrices = [];
self.key_scales = [];
#
#Stores an animation, which blends channels together over time
#
class cAnimation:
class cKBlend:
def __init__(self):
self.index = -1;
self.time = 0.0;
self.blend = 1.0;
class cABlendChannel:
def __init__(self):
self.index = -1;
self.name = "";
self.time_start = 0.0;
self.time_end = 0.0;
self.time_scale = 1.0;
self.blends = [];
def __init__(self):
self.index = -1;
self.name = "";
self.time_start = 0.0;
self.time_end = 0.0;
self.time_scale = 1.0;
self.blendchannels = []; #cABlendChannel
#
#Stores a mesh, but only the mesh
#
class cMesh:
class cAVertex:
def __init__(self):
self.index = -1;
self.x = 0.0;
self.y = 0.0;
self.z = 0.0;
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.x)+" "+str(self.y)+" "+str(self.z);
return S;
class cANormal:
def __init__(self):
self.index = -1;
self.nx = 0.0;
self.ny = 0.0;
self.nz = 1.0;
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.nx)+" "+str(self.ny)+" "+str(self.nz);
return S;
class cATangent:
def __init__(self):
self.index = -1;
self.tx = 0.0;
self.ty = 0.0;
self.tz = 0.0;
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.tx)+" "+str(self.ty)+" "+str(self.tz);
return S;
class cATexCoord:
def __init__(self): #Can have 2, or 2+n elements!
self.index = -1;
self.u = 0.0;
self.v = 0.0;
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.u)+" "+str(self.v);
return S;
class cAColor:
def __init__(self): #Can have 3,4, or n*4 elements!
self.index = -1;
self.r = 1.0;
self.g = 1.0;
self.b = 1.0;
self.a = 1.0;
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.r)+" "+str(self.g)+" "+str(self.b)+" "+str(self.a);
return S;
class cAWeight:
def __init__(self): #Can have 3,4, or n*4 elements!
self.index = -1;
self.weights = [];
self.weight_indicies = [];
self.weight_names = [];
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = "";
for w in self.weights :
S += "w"+str(w);
S += ":";
for wi in self.weight_indicies :
S += "i"+str(wi);
S += ";";
for wn in self.weight_names :
S += str(wn)+" ";
return S;
class cAFaceVertex:
def __init__(self): #Can have 3,4, or n*4 elements!
self.index = -1;
self.coord = -1;
self.normal = -1;
self.tangent = -1;
self.texcoord = -1;
self.color = -1;
self.weight = -1;
self.bindex = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.coord) + " "+str(self.normal) + " "+str(self.tangent) + " "+str(self.texcoord) + " "+str(self.color) + " "+str(self.weight);
return S;
class cAFace:
def __init__(self):
self.index = -1;
self.vertinds = [];
self.mat = 0;
self.mode = 0;
self.image_name = "";
self.image_filename = "";
self.transp = 0;
def copy(self, old):
self.index = old.index;
self.vertinds = [ v for v in old.vertinds ];
self.mat = old.mat;
self.mode = old.mode;
self.image_name = old.image_name;
self.image_filename = old.image_filename;
self.transp = old.transp;
class cLinkVertex:
def __init__(self):
self.index = -1;
self.uv = -1;
self.co = -1;
self.no = -1;
self.tangent = -1;
self.binormal = -1;
self.color = -1;
self.weight = -1;
def get_key(self): #Return the hash key for this uv coordinate
S = str(self.co) + " "+str(self.no) + " "+str(self.tangent) + " "+str(self.uv) + " "+str(self.color) + " "+str(self.weight);
return S;
class cLinkFace:
def __init__(self):
self.index = -1;
self.indicies = None;
self.image_name = "";
self.image_filename = "";
self.material_index = 0;
self.transp = "";
self.mode = "";
def get_priority(self):
P = "";
P += str(self.transp) + "_";
P += str(self.material_index) + "_";
P += self.image_filename + " ";
P += str(self.mode) + "_";
P += str(len( self.indicies ));
return P;
class cLink:
def __init__(self):
self.index = -1;
self.weight = 0.0;
class cVAGroup:
def __init__(self):
self.index = -1;
self.name = ""
self.verts = [];
class cIAMatrixPaletteLink:
def __init__(self):
self.index = 0;
self.bone_index = 0;
self.bone_name = "";
self.arma_name = "";
class cIAGroup:
def __init__(self):
self.index = -1;
self.indicies = []; #Indicies into the IA faces
self.start_va_index = 0;
self.end_va_index = 0;
self.mat = ""; #Material name? Hm.
self._blender_material_name = ""; #Name of blender material used in this group (for resyncing)
self.mode = 0;
self.image_name = "";
self.image_filename = "";
self.transp = 0;
self.polytype = 0;
self.matrixpalette = [];
def __init__(self):
self.index = -1;
self.name = "";
self.va_coords = [];
self.va_texcoords = [];
self.va_normals = [];
self.va_tangents = [];
self.va_weights = [];
self.va_colors = [];
self.va_verts = []; #Quack! Need MULTIPLE VA's
self.ia_faces = [];
self.va_format = "";
self.ia_groups = [];
self.va_groups = [];
#self.keyframes = []; #Vertex + Normal + Tangent keyframes? Hmrgh. Simple data replace in vertex arrays?
self.material_names = {};
self.texture_names = [];
#
#Stores a texture; can be exported/converted if blender can do it
#
class cTexture:
def __init__(self):
self.index = -1;
self.name = ""; #Name of texture
self.filename = "";
self.type = "RGBA";
self.data = []; #Indicies into palette data (actual texture data; RGBA or palette data)
self.palette = {}; #Map index to RGBA color (palette if available)
self.dimensions = [0,0,0]; #dimesions (x,y,z)
self.bpp = 32; #Bits per pixel
self.epp = 4; #Elements per pixel (4 = RGBA, 3 = RGB, 2 = L,A, 1 = L );
self.hexdata = []; #Raw texture data converted to RGBA in memory
#
#Stores a material;
#
class cMaterial:
def __init__(self):
self.index = -1;
self.name = ""; #Material name
self.color_ambient = [52,52,52,255];
self.color_diffuse = [204,204,204,255];
self.color_specular = [255,255,255,255];
self.color_emissive = [0,0,0,0];
self.specularity = 32; #specular highlight 'size'
self.transparency = 1.0;#Opacity/Alpha/Transparency
self.textures = []; #List of textures (by name)
self.shaders = []; #List of shaders (by name)
self.bIndex = 0;
#
#Stores a object from the scene, includes linking information
#
class cObject:
class cAProp:
def __init__(self):
self.index = -1;
self.btype = 0;
self.name = "";
self.type = "string";
self.data = None;
class cALink:
def __init__(self):
self.index = -1;
self.btype = 0;
self.name = "";
self.type = "string";
self.data = None;
def __init__(self):
self.index = -1;
self.name = "";
self.pos = [0,0,0]; #Position
self.quat = [0,0,0,0]; #Quaternion AKA Orientation
self.matrix = [1,0,0,0,1,0,0,0,1]; #Matrix orientation
self.scale = [1,1,1]; #scale factor (local)
self.parent = "";
self.parent_bone = None;
self.depth = 0;
self.data = "";
self.data_type = "";
self.properties = [];
self.links = [];
#
#Stores a complete scene, includes linking information
#
class cScene:
def __init__(self):
self.index = -1;
self.name = "";
self.objects = [];
######################################################################
#
#Generic routines (wrappers against blenders API)
#
#--------------------------------------------------------------------
#
#Either print or write data to a file
#
def eLog( arg, data = None ):
if data != None:
data.write( arg +"\n" );
else:
print arg;
#--------------------------------------------------------------------
#
#Get the current tic
#
def eGetTime():
import time
return time.time();
#--------------------------------------------------------------------
#
#Get the current date
#
def eGetDate():
import time;
epochtime = time.time();
structime = time.gmtime( epochtime );
return '%(Y)04d-%(M)02d-%(D)02d %(h)02d:%(m)02d:%(s)02d' % \
{'Y':structime.tm_year,'M':structime.tm_mon,'D':structime.tm_mday, \
'h':structime.tm_hour,'m':structime.tm_min,'s':structime.tm_sec };
def eInstantQuit():
return Blender.Window.TestBreak();
def eStringToCString( S ):
R = "";
for c in S:
if( c == '\n' ):
R += "\\n";
elif( c == '\t' ):
R += "\\t";
elif( c == '\r' ):
R += "\\r";
elif( c == 0 ):
break;
else:
R += c;
return R;
#--------------------------------------------------------------------
#
#Use sort to sort elements with array access, by the first
#
def eSortByFirstElement( A, B ):
if( A[0] > B[0] ):
return 1;
elif( A[0] == B[0] ):
return 0;
else:
return -1;
#--------------------------------------------------------------------
#
#Use sort to sort elements with array access, by the first then second
#
def eSortByFirstThenSecondElement( A, B ):
if( A[0] > B[0] ):
return 1;
elif( A[0] == B[0] ):
if( A[1] > B[1] ):
return 1;
elif( A[1] == B[1] ):
return 0;
else:
return -1;
else:
return -1;
#--------------------------------------------------------------------
#
#Hex <-> String conversions & Hex <-> ASCII conversions
#
def eNibbleToHex( A ):
if( A >= 0 and A <= 9 ):
return chr(ord('0') + A);
else:
return chr(ord('A') + A - 10);
eChar_To_Hex = {};
eHexNibble_To_Char = { '0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'A':10, 'B':11, 'C':12, 'D':13, 'E':14, 'F':15 };
eHex_To_Char = {};
#0123456789:; 48 - 59 (0 - 11)
#ABCDEFGHIJKLMNOPQRSTUVWXYZ 65 - 90 (11 - 37)
#abcdefghijklmnopqrstuvwxyz 97 - 122 (37 - 63)
e6BitsToASCII = "0123456789:;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
#~(rle) -(save previous n) +(load from dict at n) =(copy previous n)
i = 0;
while i < 256:
hstr = eNibbleToHex( (i>>4) & 15 ) + eNibbleToHex( i & 15 );
eChar_To_Hex[ i ] = eNibbleToHex( (i>>4) & 15 ) + eNibbleToHex( i & 15 );
eHex_To_Char[ hstr ] = i;
i+=1;
def eNumbersToHex( A ):
S = "";
for a in A:
S += eChar_To_Hex[a];#eNibbleToHex( (a>>4) & 15 ) + eNibbleToHex( a & 15 );
return S;
#Incredibly simple. Not good at all, but we'll eventually use a real crypto function.
def eGenerateStringHash( S, K = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] ):
i = 0;
i_end = len(S);
while( i < i_end ):
j = i;
j_end = i + 16;
if( j_end > i_end ):
j_end = i_end;
j -= i;
j_end -= i;
while( j < j_end ):
K[j] ^= ord(S[i]);
i += 1;
j += 1;
return eNumbersToHex( K );
#--------------------------------------------------------------------
#
#Convert a binary stream (0..255 integers in a list) into ASCII binary data (6 bits per char, defined by fixed table)
#
def eBinaryToASCII( src, mode ):
#convert binary data stream (0..255 values) into 6 bit character codes
outstr = "";
currstr = "";
currstrcount = 0;
bitstream = 0;
bitcount = 0;
for c in src:
bitstream <<= 8;
bitstream |= c; #???
bitcount += 8;
while( bitcount > 5 ):
currstr += e6BitsToASCII[ (bitstream >> (bitcount - 6)) & 63 ];
currstrcount += 1;
bitcount -= 6;
#bitstream &= ((1 << bitcount) - 1); #? Don't even care.
bitstream &= 65535; #Python only; prevents it from "longing"
if( currstrcount & 256 ):
outstr += currstr;
currstrcount = 0;
currstr = "";
if( bitcount > 0 ):
if( bitcount > 6 ):
print "ERROR - bitcount in HexASCII Violated rules!";
if( bitcount > 5 ):
currstr += e6BitsToASCII[ (bitstream >> (bitcount - 6)) & 63 ];
else:
currstr += e6BitsToASCII[ (bitstream << -(bitcount - 6)) & 63 ];
outstr += currstr;
currstrcount = 0;
#Compress outstr a little (?)
#'=' = copy previous n characters. '=n'
# ? Are the next 3-66 characters repeated
#outstr[-4 to -1] == [0 to 3]
# ? Is there a RLE chance? (probably not)
return outstr;
def eASCIIToBinary( src ):
#convert 6 bit character codes into binary data stream (0..255 values)
#Generate table that converts charindex -> binary data index (table is faster than map, in C.)
fasTable = {};
c_index = 0;
for elm in e6BitsToASCII:
#! Bitreverse c_index ?
fasTable[ ord(elm) ] = c_index;
c_index += 1;
#Decompress the data
outdat = [];
bitstream = 0;
bitcount = 0;
for c in src:
bitstream <<= 6;
bitstream |= ( fasTable[ ord(c) ] & 63 );
bitcount += 6;
while( bitcount > 7 ):
bitcount -= 8;
outdat.append( (bitstream >> bitcount) & 255 );
bitstream &= 65535; #Python only; prevents it from "longing"
if( bitcount > 0 ):
bitcount -= 8;
if( bitcount >= 0 ):
outdat.append( (bitstream >> bitcount) & 255 );
else:
bitcount = -bitcount;
outdat.append( (bitstream << bitcount) & 255 );
return outdat;
#--------------------------------------------------------------------
#
#Convert a binary stream (0..255 integers in a list) into ASCII binary data (6 bits per char, defined by fixed table)
# Optionally, defined a compression method (RLE, LZSS) to compress the binary data, which is output in ASCII
#
def eASCIICompress( src, compressmode = 0 ):
#
#Down-convert bytes into 6 bit integers,
#
#Convert ascii character to numerical value
#CTON = { '0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'A':10, 'B':11, 'C':12, 'D':13, 'E':14, 'F':15 };
retascii = "";
if( compressmode != 0 ):
#LZSS would be the most effective compression method. Which is just a read + jump back; very easy to decompress.
#Compression takes a lot of string comparisons though.
#
#the nice part, is we can use binary LZSS and then convert the final result into ASCII data;
#So basically, we use regular binary compression routines, THEN convert the final binary result into ASCII hex data (6 bits per char)
#
#[LZSS (1)][ count (1) ][ back(2) ] '!'
#[ RLE (1) ][ count (1) ] '~'
#[ BACK (1) ][ count (1) ] '=' #Copy previous count + (3 to 66) characters to output
#[ SAV (1)][ count(1) ] '-' #Save last n bytes
#[ LOAD (1)][ index(1) ] '+' #Load from position in save stack (which is a constantly rotating stack of 0-63 strings)
#
findata = [];
i_read = 0;
i_check = 0;
#Move i_check to check for future copies; 3-66 chars required.
#Write i_read if no copies found.
#
#NOT IMPORTANT YET
#
#for b in src:
#Bytewise compression... not characterwise. Hmrgh.
# findata.append( b );
return eBinaryToASCII( src, 0 );
else:
return eBinaryToASCII( src, 0 );
#--------------------------------------------------------------------
#
#Return the normalized float[9] matrix [Xx Xy Xz Yx Yy Yz Zx Zy Zz]
#
def eMatrixNormalize( inmat ):
xm = math.sqrt( inmat[0]*inmat[0] + inmat[1]*inmat[1] + inmat[2]*inmat[2] );
ym = math.sqrt( inmat[3]*inmat[3] + inmat[4]*inmat[4] + inmat[5]*inmat[5] );
zm = math.sqrt( inmat[6]*inmat[6] + inmat[7]*inmat[7] + inmat[8]*inmat[8] );
outmat = [0,0,0,0,0,0,0,0,0];
if( xm > 0 ):
outmat[0] = inmat[0] / xm;
outmat[1] = inmat[1] / xm;
outmat[2] = inmat[2] / xm;
if( ym > 0 ):
outmat[3] = inmat[3] / ym;
outmat[4] = inmat[4] / ym;
outmat[5] = inmat[5] / ym;
if( zm > 0 ):
outmat[6] = inmat[6] / zm;
outmat[7] = inmat[7] / zm;
outmat[8] = inmat[8] / zm;
return outmat;
#--------------------------------------------------------------------
#
#Converts a +y facing quaternion to a +x facing quaternion (Via axis-angle conversions)
#
def eConvertQuatPoseToCustom( A ):
a = 0;
x = A.x;
y = A.y;
z = A.z;
#
#clamp angle
#
if( A.w >= 1.0 ):
a = 0;
elif( A.w <= -1.0 ):
a = math.pi;
else:
a = 2 * math.acos( -A.w );
#
#Get Magnitude (we know what it is now...)
#
sq = 1.0 - A.w*A.w;
if( sq < 0.0 ):
sq = 0;
sq = math.sqrt( sq );
if( sq > 0.0 ):
sq = 1.0/sq; #The less division the better
x *= sq;
y *= sq;
z *= sq;
#
#Normalize axis!
#
magsq = x*x + y*y + z*z;
if magsq > 0.0:
magsq = math.sqrt( magsq );
else:
magsq = 1.0;
x /= magsq;
y /= magsq;
z /= magsq;
#
#Convert back, but rotate coordinate system to OURS.
#
a2 = a/2.0;
s2 = math.sin( a2 );
B = Mathutils.Quaternion()
B.x = ( y * s2 );
B.y = ( -x * s2 );
B.z = ( z * s2 );
B.w = ( -math.cos( a2 ) );
B.normalize();
return B;
#--------------------------------------------------------------------
#
#Set the blender progress bar to display a percentage and message
#
def eDisplayProgressBar( val, msg ):
Blender.Window.DrawProgressBar( val, msg );
#--------------------------------------------------------------------
#
#Set the blender progress bar to display a percentage and message
#
def eDisplayProgressBarOption( val, msg, doit ):
if( doit ):
Blender.Window.DrawProgressBar( val, msg );
#--------------------------------------------------------------------
#
#Create a one time popup message to indicate errors or completion
#
def eDisplayPopUp( msg ):
Blender.Draw.PupMenu( msg );
#--------------------------------------------------------------------
#
#Create a one time file name selection window
#
def eDisplayFileSelector( retfunc, strtitle, strfilename ):
Blender.Window.FileSelector( retfunc, strtitle, strfilename)
#--------------------------------------------------------------------
#
#Create a one time file name selection window
#
def eDisplayFullPopup( puptitle, pupmenu ):
return Blender.Draw.PupBlock( puptitle, pupmenu);
#--------------------------------------------------------------------
#
#Create a one time file name selection window
#
def eDisplayCreate( componenttype ):
return Blender.Draw.Create( componenttype );
#--------------------------------------------------------------------
#
#Create a new internal text file
#
def eCreateNewInternalText( filename, replaceit = 1 ):
if( replaceit ):
try:
ttxt = Blender.Text.Get( filename );
except:
ttxt = None;
if ttxt != None:
Blender.Text.unlink( ttxt );
return Blender.Text.New( filename );
#--------------------------------------------------------------------
#
#Create a new external text file
#
def eCreateNewExternalText( filename ):
return open( filename, "wb" );
#
#Returns the matrix to multiply any vector or matrix by to convert from blender -> forward/up coordinate system.
#Luckily, this is just a set of matrices with 1's all the way through.
#
def eConvertCoordinates( forward, up ):
#forward #"-X" "-Y" "-Z" "+X" "+Y" "+Z"
#up #"-X" "-Y" "-Z" "+X" "+Y" "+Z"
#if( (len( forward ) == 2) and (len( up ) == 2) ):
# afwd = ord(forward[1]) - ord('X');
# aup = ord(up[1]) - ord('X');
# if( forward[0] == '-' ):
# forward += 3;
# if( up[0] == '-' ):
# aup += 3;
#
# Matrix: [Xx Xy Xz Yx Yy Yz Zx Zy Zz xi yi zi xs ys zs Xxs Xys Xzs Yxs Yys Yzs Zxs Zys Zzs]
# xi yi zi is the index swap table; xs ys zs is the multiplicand (1, -1) after swapping
#
retv = None;
if( forward == "-X" ):
if( up == "-Y" ): #-X forward, -Y up
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -X -Y not defined yet!";
if( up == "-Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -X -Z not defined yet!";
if( up == "+Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -X +Y not defined yet!";
if( up == "+Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -X +Z not defined yet!";
if( forward == "-Y" ):
if( up == "-X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Y -X not defined yet!";
if( up == "-Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Y -Z not defined yet!";
if( up == "+X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Y +X not defined yet!";
if( up == "+Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Y +Z not defined yet!";
if( forward == "-Z" ):
if( up == "-X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Z -X not defined yet!";
if( up == "-Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Z -Y not defined yet!";
if( up == "+X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Z +X not defined yet!";
if( up == "+Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform -Z +Y not defined yet!";
if( forward == "+X" ):
if( up == "-Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +X -Y not defined yet!";
if( up == "-Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +X -Z not defined yet!";
if( up == "+Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +X +Y not defined yet!";
if( up == "+Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1]; #correct; no change needed (default correctness)
if( forward == "+Y" ):
if( up == "-X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Y -X not defined yet!";
if( up == "-Z" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Y -Z not defined yet!";
if( up == "+X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Y +X not defined yet!";
if( up == "+Z" ):
retv = [3,4,5, 0,1,2, 6,7,8, 1,0,2, 1,-1,1, 1,1,1, -1,-1,-1, 1,1,1]; #INCORRECT Default bone transform (+Y +X)
#Rotate about +Z local by 90 CCW; which:
#+Y => +X
#-X => +Y
#+Z => +Z
#print "#WARNING Transform +Y +Z not defined yet!";
if( forward == "+Z" ):
if( up == "-X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Z -X not defined yet!";
if( up == "-Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Z -Y not defined yet!";
if( up == "+X" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Z +X not defined yet!";
if( up == "+Y" ):
retv = [0,1,2, 3,4,5, 6,7,8, 0,1,2, 1,1,1, 1,1,1, 1,1,1, 1,1,1];
print "#WARNING Transform +Z +Y not defined yet!";
return retv;
#
#Returns the converted vector vector (float[3])
#
def eConvertCoordinatesVector( invector, intransmatrix ):
return [ intransmatrix[12] * invector[ intransmatrix[9] ], intransmatrix[13] * invector[ intransmatrix[10] ], intransmatrix[14] * invector[ intransmatrix[11] ] ];
def eConvertQuatToMatrix( inquat ):
#ERROR
return [1,0,0, 0,1,0, 0,0,1];
#
#Returns the converted matrix (float[9])
#
def eConvertCoordinatesMatrix( inmatrix, intransmatrix ):
return [ intransmatrix[15]*inmatrix[ intransmatrix[0] ], intransmatrix[16]*inmatrix[ intransmatrix[1] ], intransmatrix[17]*inmatrix[ intransmatrix[2] ], intransmatrix[18]*inmatrix[ intransmatrix[3] ], intransmatrix[19]*inmatrix[ intransmatrix[4] ], intransmatrix[20]*inmatrix[ intransmatrix[5] ], intransmatrix[21]*inmatrix[ intransmatrix[6] ], intransmatrix[22]*inmatrix[ intransmatrix[7] ], intransmatrix[23]*inmatrix[ intransmatrix[8] ] ];
#
#Returns the converted quaternion vector (float[4])
#
def eConvertCoordinatesQuaternion( inquat, intransmatrix ):
#Most complicated; requires converting to matrix, then to quaternion???
#QUAT -> MATRIX
qx = inquat[0];
qy = inquat[1];
qz = inquat[2];
qw = inquat[3];
xx = qx*qx;
xx += xx;
yy = qy*qy;
yy += yy;
zz = qz*qz;
zz += zz;
Xx = 1.0 - yy - zz;#m[0]
Yy = 1.0 - xx - zz;#m[4]
Zz = 1.0 - xx - yy;#m[8]
xx = qx*qy; #xx = xy //xx = float xy = x*y;
xx += xx;
yy = qz*qw; #yy = zw //zz = float zw = z*w;
yy += yy;
Yx = xx - yy;#m[1]
Xy = xx + yy;#m[3]
xx = qx*qz; #xx = xz //xx = float xz = x*z;
xx += xx;
yy = qy*qw; #xx = yw //yy = float yw = y*w;
yy += yy;
Zx = xx + yy;#m[2]
Xz = xx - yy;#m[6]
yy = qy*qz; #yy = yz //yy = float yz = y*z;
yy += yy;
xx = qx*qw; #xx = zw //xx = float xw = x*w;
xx += xx;
Zy = yy - xx;#m[5]
Yz = yy + xx;#m[7]
M = [ Xx, Xy, Xz, Yx, Yy, Yz, Zx, Zy, Zz ];
#CONVERT MATRIX
R = eConvertCoordinatesMatrix( M, intransmatrix );
#MATRIX -> QUAT
Xx = R[0];
Xy = R[1];
Xz = R[2];
Yx = R[3];
Yy = R[4];
Yz = R[5];
Zx = R[6];
Zy = R[7];
Zz = R[8];
qw = math.sqrt( 1.0 + Xx + Yy + Zz );#1.0f + m[0] + m[4] + m[8]
w4 = ( 2.0 * qw );
qw *= 0.5;
if( w4 != 0 ):
w4 = 1.0 / w4;
qx = (Yz - Zy) * w4;#(m[7] - m[5])
qy = (Zx - Xz) * w4;#(m[2] - m[6])
qz = (Xy - Yx) * w4;#(m[3] - m[1])
else:
#Bad matrix4x4 happened, do nothing? #ERROR, degenrate case?
pass;
#mulq = ( 1.5 - 0.5*( qx*qx + qy*qy + qz*qz + qw*qw ) ) ;
#Math::quatNormalize( q ); #Renormalize! this is a unit quaternion
qmag = math.sqrt(qx*qx + qy*qy + qz*qz + qw*qw);
if( qmag > 0 ):
qx /= qmag;
qy /= qmag;
qz /= qmag;
qw /= qmag;
return [qx,qy,qz,qw];
#
#Returns the converted scale vector (float[3])
#
def eConvertCoordinatesScale( inscale, intransmatrix ):
return [ intransmatrix[12] * inscale[ intransmatrix[9] ], intransmatrix[13] * inscale[ intransmatrix[10] ], intransmatrix[14] * inscale[ intransmatrix[11] ] ];
#
#Convert blender data into custom classes in memory
#
def eConvertBlender():
global BDATA_armatures;
global BDATA_channels;
global BDATA_animations;
global BDATA_meshes;
global BDATA_textures;
global BDATA_materials;
global BDATA_models;
global BDATA_scenes;
global GLOBAL_DEBUG_SHOW_PROGRESS;
global GLOBAL_TRANSFORM_BONE_FORWARD; #"-X" "-Y" "-Z" "+X" "+Y" "+Z"
global GLOBAL_TRANSFORM_BONE_UP; #"-X" "-Y" "-Z" "+X" "+Y" "+Z"
global GLOBAL_MAX_SPLIT_BONES;
global GLOBAL_QUIT;
global GLOBAL_CONVERT_QUADS;
progress_start = 0.0;
progress_end = 0.0;
progress_count = 1;
progress_curr = 0;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Starting",GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
BDATA_armatures = [];
BDATA_armatures_curr = 0;
BDATA_armatures_map = {}; #Map blender name -> index into armatures
BDATA_channels = [];
BDATA_channels_curr = 0;
BDATA_channels_map = {}; #Map blender name -> index into channels
BDATA_animations = [];
BDATA_animations_curr = 0;
BDATA_animations_map = {}; #Map blender name -> index into animations
BDATA_meshes = [];
BDATA_meshes_curr = 0;
BDATA_meshes_map = {}; #Map blender name -> index into meshes
BDATA_textures = [];
BDATA_textures_curr = 0;
BDATA_textures_map = {}; #Map blender name -> index into textures
BDATA_materials = [];
BDATA_materials_curr = 0;
BDATA_materials_map = {}; #Map blender name -> index into materials
BDATA_models = [];
BDATA_models_curr = 0;
BDATA_models_map = {}; #Map blender name -> index into models
BDATA_scenes = [];
BDATA_scenes_curr = 0;
BDATA_scenes_map = {}; #Map blender name -> index into scenes
#armatures
#channels
#animations
#shapes (physics shapes?)
#meshes
#textures
#materials
#models
#scenes
#
#First obtain the object list we want to export:
#
#all selected objects
blender_selected_objects = [];
#specialized selected object lists
blender_selected_curve_objects = [];
blender_selected_armature_objects = [];
blender_selected_mesh_objects = [];
blender_selected_lamp_objects = [];
blender_selected_camera_objects = [];
blender_selected_surface_objects = [];
blender_selected_metaball_objects = [];
blender_selected_text_objects = [];
blender_selected_empty_objects = [];
progress_start = 0.0;
progress_end = 1.0;
progress_count = 1;
progress_curr = 0;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Objects",GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
MAT_allnames = {};
#BLENDERFPS
BLENDERFPS = Blender.Scene.GetCurrent().render.framesPerSec();
#Determine what we are trying to accomplish;
blender_current_objects = None;
if( len( Blender.Object.GetSelected() ) > 0 ):
#Get only selected objects
blender_current_objects = Blender.Object.GetSelected();
else:
#get all objects in scene
blender_current_objects = Blender.Object.Get();
map_bone_parented_objects_to_armature_object_name = {};
if( blender_current_objects != None ):
#Get only selected objects
blender_sortobjects = [];
for selobj in blender_current_objects:
if( selobj != None ):
D = 0;
C = selobj;
MaxDepth = 0;
while C.getParent() != None: #blender enforces non-looped trees; this is safe
C = C.getParent();
D += 1;
MaxDepth += 1;
if( MaxDepth > 1024 ):
break;
blender_sortobjects.append( [selobj, D] );
#Sort objects by priority then depth
TYPE_PRIORITY = {
'Curve':10,
'Armature':9,
'Mesh':8,
'Lamp':7,
'Camera':6,
'Surface':5,
'Metaball':4,
'Text':3,
'Empty':0,
};
#Sorting function by priority then depth
def blendobjsunsortedcompare( A, B ):
try:
nA = TYPE_PRIORITY[ A[0].type ];
except:
nA = 0;
try:
nB = TYPE_PRIORITY[ B[0].type ];
except:
nB = 0;
if( nA > nB ):
return 1;
elif( nA == nB ):
if( A[1] > B[1] ):
return 1;
elif( A[1] == B[1] ):
return 0;
else:
return -1;
else:
return -1;
blender_sortobjects.sort( blendobjsunsortedcompare );
#Return object list; sorted first by depth, second by priority
retObjs = [];
for objpair in blender_sortobjects:
if( objpair[0] != None ):
blender_selected_objects.append( objpair[0] );
if( objpair[0].type == 'Curve' ):
blender_selected_curve_objects.append( objpair[0] );
elif( objpair[0].type == 'Armature' ):
blender_selected_armature_objects.append( objpair[0] );
elif( objpair[0].type == 'Mesh' ):
blender_selected_mesh_objects.append( objpair[0] );
elif( objpair[0].type == 'Lamp' ):
blender_selected_lamp_objects.append( objpair[0] );
elif( objpair[0].type == 'Camera' ):
blender_selected_camera_objects.append( objpair[0] );
elif( objpair[0].type == 'Surface' ):
blender_selected_surface_objects.append( objpair[0] );
elif( objpair[0].type == 'Metaball' ):
blender_selected_metaball_objects.append( objpair[0] );
elif( objpair[0].type == 'Text' ):
blender_selected_text_objects.append( objpair[0] );
else:
blender_selected_empty_objects.append( objpair[0] );
for cc in objpair[0].constraints:
if( cc.type == Blender.Constraint.Type.CHILDOF ):
#If this is a child to a armature bone, it may be a spline point intermediary object
targobj = cc[ Blender.Constraint.Settings.TARGET ];
targbone = cc[ Blender.Constraint.Settings.BONE ];
if( targbone != None ):
#definitely a spline point to a armature object.
map_bone_parented_objects_to_armature_object_name[ objpair[0].name ] = [targobj.name, targbone];
#IKSOLVER?
#
#Now we have all selected objects, broken up by type and depth sorted, which contains all blender objects to export.
#
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_curve_objects) + 1;
progress_curr = 0;
for obj in blender_selected_curve_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Curve: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Need to check curves for vertex hooks that hook into armatures.
#Maintain a map of curve hooks into armature objects; These will define splinepoints.
#map_bone_parented_objects_to_armature_object_name[ cc.name ] = [targobj, targbone];
pass;
#
#Requires collecting all the known IK chains and data from each armature object; interestingly enough;
#Armature 'data' (not objects) have the same bones and bone data;
#Armature 'objects' each have different bone constrains and IK chains and limits.
#
#So seperate these two out. #ERROR #LAST
#
map_armature_object_to_ikchains = {};
map_armature_object_name_to_armature_data = {};
map_armature_name_to_armature_data = {};
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_armature_objects) + 1;
progress_curr = 0;
for obj in blender_selected_armature_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Armature: " + obj.name,GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Armature objects just need bones, ik_chain and some assigned animation data.
barma = obj.getData(0,1);
if( barma != None ):
map_bone_name_to_new_index = {};
if( not barma.name in BDATA_armatures_map ):
#
#Clear object location
#
OFFSET_LOCATION = obj.getLocation('localspace');
OFFSET_SCALE = obj.getSize('localspace');
OFFSET_EULER = obj.getEuler('localspace');
obj.setLocation([0,0,0]);
obj.setEuler( Mathutils.Euler(0,0,0) );
obj.setSize( 1,1,1 );
#
#Create exportable data:
#
exporteddata = cArmature();
exporteddata.name = barma.name;
allbones = barma.bones.values();
numbones = len( allbones );
#
#Generate bone depth
#
bonessorted = [];
for B in allbones:
D = 0;
C = B;
X = 0;
while C.parent != None: #blender enforces non-looped trees; this is safe
C = C.parent;
D += 1;
X += 1;
if( X > numbones ):
break;
bonessorted.append([D, B.name]);
#
#Create a sorted array of bone keys, in order from least to greatest depth
#
def boneunsortedcompare( A, B ):
if( A[0] > B[0] ):
return 1;
elif( A[0] == B[0] ):
if( A[1] > B[1] ):
return 1;
elif( A[1] == B[1] ):
return 0;
else:
return -1;
else:
return -1;
#
bonessorted.sort( boneunsortedcompare );
#
#Now we have a sorted array of bones by depth;
#
#Element 0 is depth, element 1 is the bone name
#
#map_bone_to_fixed_transform = {};#Since we repair bone transforms, we have to repair the animation keys too.
#Grab all bones, convert to local coordinate system
bone_index_curr = 0;
for Bdata in bonessorted:
Bname = Bdata[1];
bBone = barma.bones[ Bname ];
bone_mat = bBone.matrix['ARMATURESPACE'];
B = cArmature.cBone();
B.index = bone_index_curr;
B.name = bBone.name;
exporteddata._map_bone_name_to_index[ B.name ] = B.index;
map_bone_name_to_new_index[ B.name ] = B.index;
tmpp = bone_mat.translationPart();
B.pos = [tmpp[0], tmpp[1], tmpp[2]];
tmpm = bone_mat.rotationPart();
B.matrix = [tmpm[0][0], tmpm[0][1], tmpm[0][2],tmpm[1][0], tmpm[1][1], tmpm[1][2],tmpm[2][0], tmpm[2][1], tmpm[2][2]];
B.scalemode = 0; #Can we figure this out? Eh.
#bone.scalemode = 2;
#for optn in bBone.options:
# if optn == Blender.Armature.CONNECTED: #IK to parent!
# pass;
# elif optn == Blender.Armature.HINGE: #No rotation or scale inherited (makes this bone a root bone? huh.)
# bone.scalemode = 0;
# elif optn == Blender.Armature.NO_DEFORM:
# pass;
B.radius_start = bBone.headRadius;
B.radius_end = bBone.tailRadius;
B.length = bBone.length;
B.child_count = 0;
B.depth = Bdata[0];
B.parent_index = B.index;
if( bBone.parent != None ):
B.parent = bBone.parent.name;
if( bBone.parent.name in exporteddata._map_bone_name_to_index ):
B.parent_index = exporteddata._map_bone_name_to_index[ bBone.parent.name ];
else:
B.parent = None;
exporteddata.bones.append( B );
#
#IK limits are in posebones;
#
#bone = gArmature.gBone();
#bone.name = boneelement[1];
#bone.pos = bone_mat.translationPart(); #bone start position(relative to armature)
#Quat = ConvertMatrixToQuaternion( bone_mat_world, True ); #Hm! World matrix, so convert
#bone.quat = [Quat.x,Quat.y,Quat.z,Quat.w];
#bone.scale = 0; #*Scaling mode! Set to default? (no propogation)
#
#More complex bone information (chains, IK, subdivision, constraints, splines...)
#
#bone.length = bBone.length;
#bone.depth = boneelement[0];
#bone.splinepoint = [0,0,0]; #Hm, resides at root? where does this come from?
#
#IK limits are in the Pose system, not armature... Huh.
#
#bone.limr = [];
#bone.limp = [];
#bone.scalemode = 2;
#for optn in bBone.options:
# if optn == Armature.CONNECTED: #IK to parent!
# pass;
# elif optn == Armature.HINGE: #No rotation or scale inherited (makes this bone a root bone? huh.)
# bone.scalemode = 0;
# elif optn == Armature.NO_DEFORM:
# pass;
#
#if bBone.subdivisions > 1 :
# pass;
bone_index_curr += 1;
#
#Generate child count
#
for B in exporteddata.bones:
ci = B.index;
while( exporteddata.bones[ ci ].parent_index != exporteddata.bones[ ci ].index ):
ci = exporteddata.bones[ ci ].parent_index;
exporteddata.bones[ ci ].child_count += 1;
#
#Save class for export
#
exporteddata.index = BDATA_armatures_curr;
map_armature_object_name_to_armature_data[ obj.name ] = exporteddata.index;
map_armature_name_to_armature_data[ exporteddata.name ] = exporteddata.index;
BDATA_armatures.append( exporteddata );
BDATA_armatures_map[ exporteddata.name ] = BDATA_armatures_curr;
BDATA_armatures_curr += 1;
#
#Restore object location
#
obj.setLocation( OFFSET_LOCATION );
obj.setEuler( OFFSET_EULER );
obj.setSize( OFFSET_SCALE );
else:
armobj = BDATA_armatures_map[ barma.name ];
map_armature_object_name_to_armature_data[ obj.name ] = armobj.index;
#map_armature_name_to_armature_data[ barma.name ] = armobj.index;
#for B in armobj.bones:
# map_bone_name_to_new_index[ B.name ] = B.index;
#
# PROBLEM:
#
# Armatures have some data that is fixed;
# But IK limits are not fixed into the armature, but rather the object that has the armatue.
# So pose and IK limits are unique PER OBJECT, not per armature.
#
bpose = None;
try:
bpose = obj.getPose();
except:
bpose = None;
print "No Pose";
#Must have a pose set (?)
if( bpose != None ):
pbones = bpose.bones.values();
posebonedata = {};
#Uh! hm.
for pbone in pbones:
#if( pbone.constraints )
#map_bone_name_to_new_index[ B.name ] = B.index;
#bonessorted
if( pbone.name in map_bone_name_to_new_index ):
sidex = map_bone_name_to_new_index[ pbone.name ];
#exporteddata.bones[ sidex ];
#Spline points:
#AnyObject -> ChildOf Constraint, to armature, to bone.
#Required to define a actual spline constraint/spline point.
#Modifyer on curve -> Hook to AnyObject (curve part not nessecary)
ikchain = cArmature.cIKChain();
ikchain.name = pbone.name;
ikchain.startbone = pbone.name;
ikchain.endbone = "";
ikchain.method = 0;
ikchain.iklim_pos_min =[0.0,0.0,0.0];
ikchain.iklim_pos_max =[0.0,0.0,0.0];
ikchain.iklim_pos_stiff =[0.0,0.0,0.0];
ikchain.iklim_rot_min =[0.0,0.0,0.0];
ikchain.iklim_rot_max =[0.0,0.0,0.0];
ikchain.iklim_rot_stiff =[0.0,0.0,0.0];
if( pbone.limitX ):
exporteddata.bones[ sidex ].iklim_rotXmin = pbone.limitMin[0]; #Limit rotation
exporteddata.bones[ sidex ].iklim_rotXmax = pbone.limitMax[0];
ikchain.iklim_rot_stiff[0] = pbone.stiffX;
ikchain.iklim_rot_min[0] = pbone.limitMin[0];
ikchain.iklim_rot_max[0] = pbone.limitMax[0];
if( pbone.limitY ):
exporteddata.bones[ sidex ].iklim_rotYmin = pbone.limitMin[1];
exporteddata.bones[ sidex ].iklim_rotYmax = pbone.limitMax[1];
ikchain.iklim_rot_stiff[1] = pbone.stiffY;
ikchain.iklim_rot_min[1] = pbone.limitMin[1];
ikchain.iklim_rot_max[1] = pbone.limitMax[1];
if( pbone.limitZ ):
exporteddata.bones[ sidex ].iklim_rotZmin = pbone.limitMin[2];
exporteddata.bones[ sidex ].iklim_rotZmax = pbone.limitMax[2];
ikchain.iklim_rot_stiff[2] = pbone.stiffZ;
ikchain.iklim_rot_min[2] = pbone.limitMin[2];
ikchain.iklim_rot_max[2] = pbone.limitMax[2];
if( pbone.lockXRot ):
#?
pass;
if( pbone.lockYRot ):
#?
pass;
if( pbone.lockZRot ):
#?
pass;
if( pbone.hasIK ):
#Part of an IK chain! Save that information.
for constra in pbone.constraints:
#constra.influence;
#constra.name;
#constra.type;
if( constra.type == Blender.Constraint.Type.TRACKTO ):
ikchain2 = cArmature.cIKChain();
ikchain2.copy( ikchain );
ikchain2.name = constra.name + "@" + ikchain.name;
targobj = constra[Blender.Constraint.Settings.TARGET];
targobjbone = constra[Blender.Constraint.Settings.BONE];
#TrackTo implies a "eye" configuration?
posebonedata[ ikchain2.name ] = ikchain2;
if( constra.type == Blender.Constraint.Type.IKSOLVER ):
ikchain2 = cArmature.cIKChain();
ikchain2.copy( ikchain );
ikchain2.name = constra.name + "@" + ikchain.name;
chainlen = constra[ Blender.Constraint.Settings.CHAINLEN ];
iterations = constra[ Blender.Constraint.Settings.ITERATIONS ];
usetip = constra[ Blender.Constraint.Settings.USETIP ];
#IK chain is useful.
if( usetip ):
ikchain2.depth = chainlen - 1;
ikchain2.startbone = pbone.name;
else:
ikchain2.depth = chainlen;
#ikchain.method = usetip; #?
#Find the end bone? Hm.
ikchain2.endbone = "";
posebonedata[ ikchain2.name ] = ikchain2;
#Blender.Constraint.Type.TRACKTO;
#offs = constra[Constraint.Settings.TARGET];
#offs = constra[Constraint.Settings.BONE];
#Blender.Constraint.Type.IKSOLVER;
#offs = constra[Constraint.Settings.OFFSET];
#offs = constra[Constraint.Settings.TOLERANCE];
#offs = constra[Constraint.Settings.ITERATIONS];
#offs = constra[Constraint.Settings.CHAINLEN];
#offs = constra[Constraint.Settings.POSWEIGHT];
#offs = constra[Constraint.Settings.ROTWEIGHT];
#offs = constra[Constraint.Settings.ROTATE];
#offs = constra[Constraint.Settings.USETIP];
#TRACKTO, IKSOLVER, FOLLOWPATH, COPYROT, COPYLOC, COPYSIZE, ACTION, LOCKTRACK, STRETCHTO, FLOOR, LIMITLOC, LIMITROT, LIMITSIZE, LIMITDIST, CLAMPTO, PYTHON, CHILDOF, TRANSFORM, NULL
#pbone.stretch
#
#Define spline point with ? A curve + vertex hooks to bones?
#
#pbone.splinepoints = [0,0,0];
posebonedata[ ikchain.name ] = ikchain;
else:
print "ERROR - nonexistant bone "+pbone.name+"in pose!";
map_armature_object_to_ikchains[ obj.name ] = [ barma.name, posebonedata];
progress_start = 0.0;
progress_end = 1.0;
progress_count = 1;
progress_curr = 0;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Armature data...", GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#
#Now let's go back and add ik chains to compatible armatures (?)
#
for delem in map_armature_object_to_ikchains.values():
armaname = delem[0];
pbonedata = delem[1];
amadata = BDATA_armatures_map[ armaname ];
for ikdata in pbonedata.keys():
BDATA_armatures[ BDATA_armatures_map[ armaname ] ].ikchains.append( pbonedata[ ikdata ] );
#
#Now, go figure out if we have any spline points to worry about:
#
for bponame in map_bone_parented_objects_to_armature_object_name.keys():
atargd = map_bone_parented_objects_to_armature_object_name[ bponame ];
armaname = atargd[0];
bonename = atargd[1];
if( armaname in BDATA_armatures_map ):
nsp = cArmature.cSplinePoint();
nsp.name = bponame;
nsp.bone = bonename;
xobj = None;
try:
xobj = Blender.Object.Get( bponame );
except:
xobj = None;
nsp.x = 0;
nsp.y = 0;
nsp.z = 0;
nsp.vx = 0;
nsp.vy = 0;
nsp.vz = 0;
if( xobj != None ):
nsp.x = xobj.loc[0];
nsp.y = xobj.loc[1];
nsp.z = xobj.loc[2];
ml = xobj.matrixLocal;
nsp.vx = ml[0][0];
nsp.vy = ml[0][1];
nsp.vz = ml[0][2];
BDATA_armatures[ BDATA_armatures_map[ armaname ] ].splinepoints.append( nsp );
#A second required step is exporting ALL actions and animation channels + animations;
#Then once we have all the channels and animations, we can search animations versus armatures for compatible matches;
#THEN we can add compatible animations to each compatible armature.
blender_actions = Blender.Armature.NLA.GetActions();
blender_actions_keys = blender_actions.keys();
blender_actions_keys.sort();
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_actions_keys) + 1;
progress_curr = 0
for action_key in blender_actions_keys:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Actions: " + action_key, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Actions contain channels. 1 action = 1 animation
act = blender_actions[ action_key ];
act_channel_names = act.getChannelNames();
act_frame_numbers = act.getFrameNumbers();
all_channels = {};
#Fixed constant array
bez_keys = [Blender.Ipo.PO_LOCX,Blender.Ipo.PO_LOCY,Blender.Ipo.PO_LOCZ, Blender.Ipo.PO_QUATX,Blender.Ipo.PO_QUATY,Blender.Ipo.PO_QUATZ,Blender.Ipo.PO_QUATW, Blender.Ipo.PO_SCALEX,Blender.Ipo.PO_SCALEY,Blender.Ipo.PO_SCALEZ ];
#For all channel names in this action (bones, hopefully)
for channel_name in act_channel_names:
channel_keys = {}; #Map frame # to key data [ pos, quat, scale ]
IPO = act.getChannelIpo( channel_name );
if( IPO != None ):
#Default previous data to identity (bleed through)
PREV_POS_X = 0.0;
PREV_POS_Y = 0.0;
PREV_POS_Z = 0.0;
PREV_SCALE_X = 1.0;
PREV_SCALE_Y = 1.0;
PREV_SCALE_Z = 1.0;
PREV_QUAT = [0,0,0,1];
for frame in act_frame_numbers:
valid_frame = 0;
#convert from bezier points (can bake, I guess, if you want to do that.)
bez_ary = [None,None,None, None,None,None,None, None,None,None];
Bki = 0;
for Bk in bez_keys:
if IPO[ Bk ] != None:
#
#Get Bezier point indicies (direct copy, no interpol)
#
Bi = 0;
for Bz in IPO[ Bk ].bezierPoints:
if Bz.vec[1][0] == frame:
valid_frame = 1;
bez_ary[ Bki ] = Bi;
Bi += 1;
Bki += 1;
#If there were ANY keys:
if valid_frame:
fdata = [ None, None, None ];
#
#Get position if it exists (Any key)
#
if bez_ary[0] != None or bez_ary[1] != None or bez_ary[2] != None:
#
pos = [PREV_POS_X, PREV_POS_Y, PREV_POS_Z];#ERROR, need to interpolate these values
#pos = [0,0,0];
if bez_ary[0] != None:
pos[1] = -IPO[bez_keys[0]].bezierPoints[ bez_ary[0] ].vec[1][1]; #0 = -Y
PREV_POS_Y = pos[1];
if bez_ary[1] != None:
pos[0] = IPO[bez_keys[1]].bezierPoints[ bez_ary[1] ].vec[1][1]; #1 = X
PREV_POS_X = pos[0];
if bez_ary[2] != None:
pos[2] = IPO[bez_keys[2]].bezierPoints[ bez_ary[2] ].vec[1][1]; #2 = Z
PREV_POS_Z = pos[2];
#
fdata[0] = pos;
#
#Quaternions MUST have all keyframes present (#ERROR?)
#
if bez_ary[3] != None and bez_ary[4] != None and bez_ary[5] != None and bez_ary[6] != None:
#
Quatme = Mathutils.Quaternion()
Quatme.x = IPO[bez_keys[3]].bezierPoints[ bez_ary[3] ].vec[1][1];
Quatme.y = IPO[bez_keys[4]].bezierPoints[ bez_ary[4] ].vec[1][1];
Quatme.z = IPO[bez_keys[5]].bezierPoints[ bez_ary[5] ].vec[1][1];
Quatme.w = IPO[bez_keys[6]].bezierPoints[ bez_ary[6] ].vec[1][1];
Quatme.normalize();
#
#Since Blender stores +y as forward and we use +x... convert!
#
Quatfinal = eConvertQuatPoseToCustom( Quatme );
#
fdata[1] = [Quatfinal.x,Quatfinal.y,Quatfinal.z,Quatfinal.w];
PREV_QUAT = [Quatfinal.x,Quatfinal.y,Quatfinal.z,Quatfinal.w];
else:
if bez_ary[3] == None and bez_ary[4] == None and bez_ary[5] == None and bez_ary[6] == None:
#
#Valid, no rotation on this frame.
#
pass;
else:
Log( " -Incomplete Rotation, action: "+act.getName()+" frame: %d " % ( frame ), log )
pass;
#
#
#Get scale if it exists (Any key)
#
if bez_ary[7] != None or bez_ary[8] != None or bez_ary[9] != None:
#
scale = [PREV_SCALE_X, PREV_SCALE_Y, PREV_SCALE_Z];#ERROR, need to interpolate these values
#scale = [1,1,1];
if bez_ary[7] != None:
scale[1] = IPO[bez_keys[7]].bezierPoints[ bez_ary[7] ].vec[1][1]; #0 = -Y
PREV_SCALE_X = scale[1];
if bez_ary[8] != None:
scale[0] = IPO[bez_keys[8]].bezierPoints[ bez_ary[8] ].vec[1][1]; #1 = X
PREV_SCALE_Y = scale[0];
if bez_ary[9] != None:
scale[2] = IPO[bez_keys[9]].bezierPoints[ bez_ary[9] ].vec[1][1]; #2 = Z
PREV_SCALE_Z = scale[2];
#
fdata[2] = scale;
#
#Store data
#
if not frame in channel_keys:
channel_keys[ frame ] = fdata;
else:
print "ERROR - duplicate keys? how is this possible?";
#
#channel_keys -> #Map frame # to key data [ pos, quat, scale ]
#
timesortedarray = [];
for k in channel_keys.keys():
timesortedarray.append( k );
timesortedarray.sort();
if( len(timesortedarray) > 0 ):
expchannel = cChannel();
expchannel.name = action_key + "@" + channel_name; #Let's hope this is unique...
expchannel.target = channel_name;
expchannel.time_start = timesortedarray[0];
expchannel.time_end = timesortedarray[-1];
for k in timesortedarray:
keydata = channel_keys[ k ];
if( keydata[0] != None ):
ndata = cChannel.cKPosition();
ndata.time = k;
ndata.x = keydata[0][0];
ndata.y = keydata[0][1];
ndata.z = keydata[0][2];
expchannel.key_positions.append( ndata );
if( keydata[1] != None ):
ndata = cChannel.cKQuat();
ndata.time = k;
ndata.x = keydata[1][0];
ndata.y = keydata[1][1];
ndata.z = keydata[1][2];
ndata.w = keydata[1][3];
expchannel.key_quats.append( ndata );
#Add matrix key too? (hm)
#expchannel.key_matrices = []; #cKMatrix() [1,0,0,0,1,0,0,0,1]
if( keydata[2] != None ):
ndata = cChannel.cKScale();
ndata.time = k;
ndata.x = keydata[2][0];
ndata.y = keydata[2][1];
ndata.z = keydata[2][2];
expchannel.key_scales.append( ndata );
expchannel.index = BDATA_channels_curr;
BDATA_channels.append( expchannel );
BDATA_channels_map[ expchannel.name ] = BDATA_channels_curr;
all_channels[ expchannel.name ] = BDATA_channels_curr;
BDATA_channels_curr += 1;
else:
print "#WARNING zero length channel ignored: " + str(action_key) + " -> " + str( channel_name );
#
#1 action == 1 animation; Now we have all the channels that belong to this action.
#
expanim = cAnimation();
expanim.name = action_key;
expanim.time_start = 0.0;
expanim.time_end = 0.0;
expanim.time_scale = 1.0 * float( BLENDERFPS ); #Take this from the blender time syncing
expanim.blendchannels = [];
#Blender.Render.framesPerSec() Get('curtime'); #??
timefirst = 1;
min_time = 0;
max_time = 0;
for chankey in all_channels:
nbch = cAnimation.cABlendChannel();
nbch.name = chankey;
chanindex = all_channels[ chankey ];
chand = BDATA_channels[ chanindex ]; #Add this channel to this animation?
nbch.time_start = chand.time_start;
nbch.time_end = chand.time_end;
nbch.time_scale = 1.0;
if( timefirst ):
min_time = nbch.time_start;
max_time = nbch.time_end;
timefirst = 0;
else:
if( nbch.time_start < min_time ):
min_time = nbch.time_start;
if( nbch.time_start > max_time ):
max_time = nbch.time_start;
if( nbch.time_end < min_time ):
min_time = nbch.time_end;
if( nbch.time_end > max_time ):
max_time = nbch.time_end;
nbch.blends = [];
bl = cAnimation.cKBlend();
bl.time = nbch.time_start;
bl.blend = 1.0;
nbch.blends.append( bl );
expanim.blendchannels.append( nbch );
expanim.time_start = min_time;
expanim.time_end = max_time;
expanim.index = BDATA_channels_curr;
BDATA_animations.append( expanim );
BDATA_animations_map[ expanim.name ] = BDATA_animations_curr;
BDATA_animations_curr += 1;
#
#Sync up animation names to armatures as possible animations
#
for animd in BDATA_animations:
for armat in BDATA_armatures:
#Match bones to animations; if ANY match, assign as a possible animation.
isgood = 0;
for bchan in animd.blendchannels:
if bchan.name in armat._map_bone_name_to_index:
isgood = 1;
break;
if isgood:
BDATA_armatures[ armat.index ].possibleanimations.append( animd.name );
all_TEX_names = {};
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_mesh_objects) + 1;
progress_curr = 0;
for obj in blender_selected_mesh_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Mesh: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Mesh objects are the most complicated, and must be each turned into seperate data arrays;
#Then turned into indexed VA's.
#Then each face group turned into index arrays,
#Then each type of face group split up by priority (material, texture, facemode, ect...)
#
#THEN, if the mesh is armature deformed, or has a armature modifyer, we run into problems:
# -? how do we handle multiple armature meshes?
# -? if it has an armature, do we ignore (set to zero) weights of groups that don;t matrix deform this mesh?
# -? What additional options exist for this?
#
# Get armature data this way:
# BDATA_armatures[ map_armature_object_name_to_armature_data[ objectname ] = armatureindex ];
armatures_in_this_mesh = {}; #Map armature object name to index of armature data...
if( obj != None ):
#
#Check for armature(s) in modifyer stack?
#
xmod_armmod = [];
for Xmod in obj.modifiers:
if( Xmod.type == Blender.Modifier.Types.ARMATURE ):
if Xmod[ Blender.Modifier.Settings.OBJECT ] != None:
xmod_armmod.append( Xmod[ Blender.Modifier.Settings.OBJECT ] );
#
#No modifier, how about a parent to obtain information from?
#
if (len(xmod_armmod) == 0) and (obj.getParent() != None):
if obj.getParent().type == 'Armature':
xmod_armmod.append( obj.getParent() );
#
#Assign armature(s) to mesh
#
for bobj in xmod_armmod:
if( bobj.name in map_armature_object_name_to_armature_data ):
#
barma = bobj.getData(0,1);
if( barma != None ):
armatures_in_this_mesh[ barma.name ] = map_armature_name_to_armature_data[ barma.name ];
#armatures_in_this_mesh[ bobj.name ] = map_armature_object_name_to_armature_data[ bobj.name ];
#BDATA_armatures[ map_armature_object_name_to_armature_data[ objectname ] = armatureindex ];
#
#Basic mesh conversion is simple enough;
#
#convert all faces into VA verticies by index of unique vertex data;
#
#you end up with:
#
# 1. Array of each data type
# 2. va_verts indexing array which references the data arrays
# 3. ia_faces, which each face includes 3,4 or some other number of va_vert indicies
#
#this is the basic, most primitive form of a mesh.
#
bMesh = obj.getData(0,1);
if( bMesh != None ):
if( not bMesh.name in BDATA_meshes_map ):
OFFSET_LOCATION = obj.getLocation('localspace');
OFFSET_SCALE = obj.getSize('localspace');
OFFSET_EULER = obj.getEuler('localspace');
obj.setLocation([0,0,0]);
obj.setEuler( Mathutils.Euler(0,0,0) );
obj.setSize( 1,1,1 );
bMaterialsListO = obj.getMaterials(1);
bMaterialsListM = obj.getData(0,1).materials;
mesh = cMesh();
mesh.name = bMesh.name;
mesh.va_verts = [];
mesh.ia_faces = [];
m_use_uv = bMesh.faceUV; #Faces contain UV coords independant of verticies
m_use_uvsticky = bMesh.vertexUV; #Verticies are sticky UV mapped (choyote)
m_use_colors = bMesh.vertexColors; #(Verticies contain vertex colors)
m_use_tangents = m_use_uv; #Must have UV's or a link to an object
m_use_weights = True;
#
#Have to rebuild VA from faces using VA keys and such.
#
all_tangents_per_face = None;
if( m_use_tangents ):
all_tangents_per_face = bMesh.getTangents();
all_uv_layer_names = bMesh.getUVLayerNames();
all_color_layer_names = bMesh.getColorLayerNames();
all_vertgroup_names = [];#bMesh.getVertGroupNames(); #Weird, requires an object.
map_vert_to_groups = {}; #Map vertex index to all groups that deform it
#Vertex groups only occur IF the object is linked; without the object, there are lots of missing pieces of data!
try:
ball_vert_group_names = bMesh.getVertGroupNames();
except:
ball_vert_group_names = [];
#all_vertgroup_verts = {}; #Map vertex group name to verticies that influence it
for gname in ball_vert_group_names:
all_vertgroup_names.append( gname );
#VG = [];
for Gelem in bMesh.getVertsFromGroup(gname, True):
#VG.append( [ Gelem[0], Gelem[1] ] );
if( not Gelem[0] in map_vert_to_groups ):
map_vert_to_groups[ Gelem[0] ] = {};
map_vert_to_groups[ Gelem[0] ][ gname ] = Gelem[1]; #? Always true?
#
#Determine if this mesh has an armature deforming it? Hm
#
#This is actually impossible. Go figure.
#
#It requires knowing a object relationship, not a mesh data relationship.
#
#Procedure:
#for all faces;
#Generate a face-vertex-key & arrays for each type of data present.
#Map each face to it's unique vertex keys.
#There are less than nfaces*3~4 vertex keys, and faces with incidies into the face verticies.
#Now split up the faces into groups based on data present, size, material, bone groups, ect...
VA_coord = {};
VA_texcoord = {};
VA_color = {};
VA_normal = {};
VA_tangent = {};
VA_weight = {};
VA_coord_curr_index = 0;
VA_texcoord_curr_index = 0;
VA_color_curr_index = 0;
VA_normal_curr_index = 0;
VA_tangent_curr_index = 0;
VA_weight_curr_index = 0;
VA_faceverts = {};
VA_faceverts_curr_index = 0;
FACES_array = [];
FACES_array_curr_index = 0;
FACE_Mats = {};
TEX_names = {};
for f in bMesh.faces:
#f.index #bindex blender face index
#f.mat; #Material index for this face
#f.col[0]... #Must have color layer
#f.uv[0] #Must have face UV layer
#f.no; #Facet normal vector
#f.verts;
tans = None;
if( m_use_tangents ):
tans = all_tangents_per_face[ f.index ];
newFACE = cMesh.cAFace();
vi = 0;
for v in f.verts:
newBIndex = v.index; #Blender index
#New UV coordinate (layer 0?)
valid_uv = 0;
newUV = cMesh.cATexCoord();
if( m_use_uv ):
valid_uv = 1;
newUV.u = f.uv[vi][0];
newUV.v = f.uv[vi][1];
if( m_use_uvsticky ):
pass;
#New Color
valid_color = 0;
newColor = cMesh.cAColor();#[1.0, 1.0, 1.0, 1.0];
if( m_use_colors ):
valid_color = 1;
newColor.r = f.col[vi].r;
newColor.g = f.col[vi].g;
newColor.b = f.col[vi].b;
newColor.a = 1.0; #No alpha data. Sucks.
#New Coordinate
newCO = cMesh.cAVertex();
newCO.x = v.co[0];
newCO.y = v.co[1];
newCO.z = v.co[2];
#New Normal
newNO = cMesh.cANormal();
newNO.nx = v.no[0];
newNO.ny = v.no[1];
newNO.nz = v.no[2];
#New Tangent
valid_tangent = 0;
newTAN = cMesh.cATangent();
if( m_use_tangents ):
valid_tangent = 1;
newTAN.tx = tans[vi][0];
newTAN.ty = tans[vi][1];
newTAN.tz = tans[vi][2];
#New Weight:
valid_weight = 0;
newWEIGHT = cMesh.cAWeight();
try:
wpairs = bMesh.getVertexInfluences( v.index );
except:
wpairs = [];
m_use_weights = False;
for dual in wpairs:
valid_weight = 1;
newWEIGHT.weights.append( dual[1] );
newWEIGHT.weight_names.append( dual[0] );
newWEIGHT.weight_indicies.append( 0 ); #Must be same size as everything else!!
#
#check current arrays & add as needed
#
if valid_uv:
newUVKey = newUV.get_key();
if( not newUVKey in VA_texcoord ):
newUV.index = VA_texcoord_curr_index;
VA_texcoord[ newUVKey ] = newUV;
VA_texcoord_curr_index += 1;
else:
newUV = VA_texcoord[ newUVKey ];#.index;
if valid_color:
newColorKey = newColor.get_key();
if( not newColorKey in VA_color ):
newColor.index = VA_color_curr_index;
VA_color[ newColorKey ] = newColor;
VA_color_curr_index += 1;
else:
newColor = VA_color[ newColorKey ];#.index;
newCOKey = newCO.get_key();
if( not newCOKey in VA_coord ):
newCO.index = VA_coord_curr_index;
newCO.bindex = newBIndex;
VA_coord[ newCOKey ] = newCO;
VA_coord_curr_index += 1;
else:
newCO = VA_coord[ newCOKey ];#.index;
newNOKey = newNO.get_key();
if( not newNOKey in VA_normal ):
newNO.index = VA_normal_curr_index;
VA_normal[ newNOKey ] = newNO;
VA_normal_curr_index += 1;
else:
newNO = VA_normal[ newNOKey ];#.index;
if valid_tangent:
newTANKey = newTAN.get_key();
if( not newTANKey in VA_tangent ):
newTAN.index = VA_tangent_curr_index;
VA_tangent[ newTANKey ] = newTAN;
VA_tangent_curr_index += 1;
else:
newTAN = VA_tangent[ newTANKey ];#.index;
if valid_weight:
newWEIGHTKey = newWEIGHT.get_key();
if( not newWEIGHTKey in VA_weight ):
newWEIGHT.index = VA_weight_curr_index;
VA_weight[ newWEIGHTKey ] = newWEIGHT;
VA_weight_curr_index += 1;
else:
newWEIGHT = VA_weight[ newWEIGHTKey ];#.index;
#
#Create face vertex from incidies
#
nf = cMesh.cAFaceVertex();
nf.bindex = newBIndex;
nf.coord = newCO.index;
nf.normal = newNO.index;
nf.tangent = newTAN.index;
nf.texcoord = newUV.index;
nf.color = newColor.index;
nf.weight = newWEIGHT.index;
#
#Add face vertex if not unique
#
nfKey = nf.get_key();
if( not nfKey in VA_faceverts ):
nf.index = VA_faceverts_curr_index;
VA_faceverts_curr_index += 1;
VA_faceverts[ nfKey ] = nf;
else:
nf = VA_faceverts[ nfKey ];#.index;
#
#Add to face
#
newFACE.vertinds.append( nf.index );
vi += 1;
newFACE.bindex = f.index;
newFACE.mat = f.mat;
FACE_Mats[ f.mat ] = 0;
try:
#Has an assigned image per face.
if( f.image != None ):
newFACE.image_name = f.image.getName();
newFACE.image_filename = f.image.getFilename();
TEX_names[ newFACE.image_name ] = newFACE.image_filename;
except:
#No image per face.
newFACE.image_name = "";
newFACE.image_filename = "";
#f.mode #Blender.Mesh.FaceModes.
#* ALL - set all modes at once.
#* BILLBOARD - always orient after camera.
#* HALO - halo face, always point to camera.
#* DYNAMIC - respond to collisions.
#* ALPHASORT - game engine sorts these faces only.
#* INVISIBLE - invisible face.
#* LIGHT - dynamic lighting.
#* OBCOL - use object color instead of vertex colors.
#* SHADOW - shadow type.
#* SHAREDVERT - apparently unused in Blender.
#* SHAREDCOL - shared vertex colors (per vertex).
#* TEX - has texture image.
#* TILES - uses tiled image.
#* TWOSIDE - two-sided face.
#f.smooth
#f.transp #Blender.Mesh.FaceTranspModes
#* SOLID - draw solid.
#* ADD - add to background (halo).
#* ALPHA - draw with transparency.
#* SUB - subtract from background.
#* CLIP - Clipped alpha.
if( m_use_uv ):
newFACE.mode = f.mode;
newFACE.transp = f.transp;
else:
newFACE.mode = 0;
newFACE.transp = 0;
#
#Can destroy quads and replace with triangles here:
#
if GLOBAL_CONVERT_QUADS:
if( len( newFACE.vertinds ) == 4 ):
#Make a copy
extraface = cMesh.cAFace();
extraface.copy(newFACE); # Error?
#Fix verticies (?)
#
# #ERROR, converting quad->tris has three methods:
# 1. direct data, ie: [0,1,2,3] => [0,1,2],[0,2,3] (default)
# 2. Shortest distance: Split quad into triangles along shortest (diagonal) distance
# 3. Longest distance: Split quad into triangles along longest (diagonal) distance
#
#Normally, blender uses shortest distance by default.
#
NVI = newFACE.vertinds;
T0 = [ NVI[0], NVI[1], NVI[2] ];
T1 = [ NVI[0], NVI[2], NVI[3] ];
extraface.vertinds = T1;
newFACE.vertinds = T0;
#Add the extra face
extraface.index = FACES_array_curr_index;
FACES_array_curr_index += 1;
FACES_array.append( extraface );
#Add default face
newFACE.index = FACES_array_curr_index;
FACES_array_curr_index += 1;
FACES_array.append( newFACE );
#
#Now, you have:
#
# VA: A data array for all possible components
# VA_coord = {};
# VA_texcoord = {};
# VA_color = {};
# VA_normal = {};
# VA_tangent = {};
# VA_weight = {};
# faceverts: The VA verticies that combine the array data together to make a VA
# VA_faceverts = {};
# FACES: The indicies into faceverts that connect faces together; They may be 3,4 or more index faces.
# FACES_array = [];
#
#
#Grab materials names and such
#
#print bMaterialsListO;# = obj.getMaterials(1);
#print bMaterialsListM;# = obj.getData(1).materials;
#print FACE_Mats;
#TEX_names
mesh.material_names = {};
MAT_names = {};
mat_index = 0;
for mat in bMesh.materials:
if( mat != None ):
MAT_names[ mat.getName() ] = mat_index;
MAT_allnames[ mat.getName() ] = 0;
alltexes = mat.getTextures();
for mtex in alltexes:
if( mtex != None ):
if( mtex.tex != None ):
if( mtex.tex.image != None ):
#if mtex.tex.type == Blender.Texture.Types.IMAGE:
TEX_names[ mtex.tex.image.getName() ] = mtex.tex.image.getFilename();
mesh.material_names[ mat_index ] = mat.getName();
else:
mesh.material_names[ mat_index ] = None;
mat_index += 1;
#for fmindex in FACE_Mats.keys():
# print fmindex, len( bMesh.materials );
# bmat = bMesh.materials[ fmindex ];
# if( bmat != None ):
# print "Lmi: ",fmindex;
#
mesh.texture_names = TEX_names.keys();
for tname in TEX_names.keys():
all_TEX_names[ tname ] = TEX_names[tname ];
#
#Rebuild a materials index list (remove nonused materials, generate new ones that mix texture channels?)
#
# -nope.
#
#Load in VA
#
VA_coord_max = 0;
VA_coord_in_order = {};
for vd in VA_coord.values():
VA_coord_in_order[ vd.index ] = vd;
if( vd.index > VA_coord_max ):
VA_coord_max = vd.index;
if( len( VA_coord_in_order.keys() ) > 0 ): VA_coord_max += 1;
VA_texcoord_max = 0;
VA_texcoord_in_order = {};
for vd in VA_texcoord.values():
VA_texcoord_in_order[ vd.index ] = vd;
if( vd.index > VA_texcoord_max ):
VA_texcoord_max = vd.index;
if( len( VA_texcoord_in_order.keys() ) > 0 ): VA_texcoord_max += 1;
VA_color_max = 0;
VA_color_in_order = {};
for vd in VA_color.values():
VA_color_in_order[ vd.index ] = vd;
if( vd.index > VA_color_max ):
VA_color_max = vd.index;
if( len( VA_color_in_order.keys() ) > 0 ): VA_color_max += 1;
VA_normal_max = 0;
VA_normal_in_order = {};
for vd in VA_normal.values():
VA_normal_in_order[ vd.index ] = vd;
if( vd.index > VA_normal_max ):
VA_normal_max = vd.index;
if( len( VA_normal_in_order.keys() ) > 0 ): VA_normal_max += 1;
VA_tangent_max = 0;
VA_tangent_in_order = {};
for vd in VA_tangent.values():
VA_tangent_in_order[ vd.index ] = vd;
if( vd.index > VA_tangent_max ):
VA_tangent_max = vd.index;
if( len( VA_tangent_in_order.keys() ) > 0 ): VA_tangent_max += 1;
VA_weight_max = 0;
VA_weight_in_order = {};
for vd in VA_weight.values():
VA_weight_in_order[ vd.index ] = vd;
if( vd.index > VA_weight_max ):
VA_weight_max = vd.index;
if( len( VA_weight_in_order.keys() ) > 0 ): VA_weight_max += 1;
VA_faceverts_max = 0;
VA_faceverts_in_order_map = {};
for vad in VA_faceverts.values():
VA_faceverts_in_order_map[ vad.index ] = vad;
if( vd.index > VA_faceverts_max ):
VA_faceverts_max = vd.index;
if( len( VA_faceverts_in_order_map.keys() ) > 0 ): VA_faceverts_max += 1;
vi = 0;
while( vi < VA_coord_max ):
mesh.va_coords.append( VA_coord_in_order[vi] );
vi += 1;
vi = 0;
while( vi < VA_texcoord_max ):
mesh.va_texcoords.append( VA_texcoord_in_order[vi] );
vi += 1;
vi = 0;
while( vi < VA_normal_max ):
mesh.va_normals.append( VA_normal_in_order[vi] );
vi += 1;
vi = 0;
while( vi < VA_tangent_max ):
mesh.va_tangents.append( VA_tangent_in_order[vi] );
vi += 1;
vi = 0;
while( vi < VA_weight_max ):
mesh.va_weights.append( VA_weight_in_order[ vi ] );
vi += 1;
vi = 0;
while( vi < VA_color_max ):
mesh.va_colors.append( VA_color_in_order[vi] );
vi += 1;
mesh.va_verts = [];
mesh_va_curr_index = 0;
ivavert = 0;
while( ivavert < VA_faceverts_curr_index ):
if( ivavert in VA_faceverts_in_order_map.keys() ):
vad = VA_faceverts_in_order_map[ ivavert ];
nVAV = cMesh.cLinkVertex();
nVAV.uv = vad.texcoord;
nVAV.co = vad.coord;
nVAV.no = vad.normal;
nVAV.tangent = vad.tangent;
nVAV.normal = vad.normal;
nVAV.color = vad.color;
nVAV.weight = vad.weight;
nVAV.index = mesh_va_curr_index;
mesh_va_curr_index += 1;
mesh.va_verts.append( nVAV );
else:
#Non existant vertex? Weird!
print "ERROR-Non Existant VA Vertex?";
ivavert += 1;
#
#Load in IA
#
mesh.ia_faces = []; #How to reserve space? Argh!
mesh_ia_curr_index = 0;
for fad in FACES_array:
nlf = cMesh.cLinkFace();
nlf.indicies = fad.vertinds;
nlf.image_name = fad.image_name;
nlf.image_filename = fad.image_filename;
nlf.material_index = fad.mat;
nlf.mode = fad.mode;
nlf.transp = fad.transp;
nlf.index = mesh_ia_curr_index;
mesh_ia_curr_index += 1;
mesh.ia_faces.append( nlf );
#
#Build format string?
#
mesh.va_format = "";
if( m_use_uv ):
mesh.va_format += "2ft"; #TexCoord
mesh.va_format += "3fn"; #Normal
if( m_use_tangents ):
mesh.va_format += "3fT"; #Tangent
if( m_use_colors ):
mesh.va_format += "4fc"; #Color
if( m_use_weights ):
mesh.va_format += "4fW"; #Weight
mesh.va_format += "4bI"; #Weight Index
mesh.va_format += "3fv"; #Vertex
#
#Correct & generate VA groups
#
temp_va_groups = {};
for gname in all_vertgroup_names :
NG = cMesh.cVAGroup();
NG.name = gname;
temp_va_groups[ gname ] = NG;
for V in mesh.va_coords:
if( V.bindex in map_vert_to_groups ): #map_vert_to_groups = {vertex}{ gname } -> weight
groups = map_vert_to_groups[ V.bindex ];
for gname in groups.keys():
if( gname in temp_va_groups ):
NL = cMesh.cLink();
NL.index = V.index;
NL.weight = groups[ gname ];
temp_va_groups[ gname ].verts.append( NL );
mesh.va_groups = [];
for ggroup in temp_va_groups.values():
mesh.va_groups.append( ggroup );
#
#Now we have the VA and IA that define this mesh. NOW, sorting options can exist.
#
mesh.ia_groups = [];
MFACES_sort_priority = [];
for fik in mesh.ia_faces:
MFACES_sort_priority.append( [fik.get_priority(), fik.index] );
MFACES_sort_priority.sort( eSortByFirstElement );
#
#Now split into groups based on those priorities
#
prev_index_start = 0;
prev_key = None;
if( len(MFACES_sort_priority) > 0 ):
prev_key = MFACES_sort_priority[0][0];
curr_indicies = [];
curr_group_index = 0;
for fdeya in MFACES_sort_priority:
currkey = fdeya[0];
currindex = fdeya[1];
if( currkey != prev_key ):
NG = cMesh.cIAGroup();
OFI = mesh.ia_faces[ prev_index_start ];
NG.indicies = curr_indicies;
NG.mat = OFI.material_index;
NG.mode = OFI.mode;
NG.image_name = OFI.image_name;
NG.image_filename = OFI.image_filename;
NG.transp = OFI.transp;
NG.polytype = len( OFI.indicies );
NG.index = curr_group_index;
NG.start_va_index = 0;
NG.end_va_index = len( mesh.va_verts );
curr_group_index += 1;
mesh.ia_groups.append( NG );
prev_index_start = currindex;
curr_indicies = [];
prev_key = currkey;
curr_indicies.append( currindex );
if( len(curr_indicies) > 0 ):
NG = cMesh.cIAGroup();
OFI = mesh.ia_faces[ prev_index_start ];
NG.indicies = curr_indicies;
NG.mat = OFI.material_index;
NG.mode = OFI.mode;
NG.image_name = OFI.image_name;
NG.image_filename = OFI.image_filename;
NG.transp = OFI.transp;
NG.index = curr_group_index;
NG.polytype = len( OFI.indicies );
NG.start_va_index = 0;
NG.end_va_index = len( mesh.va_verts );
curr_group_index += 1;
mesh.ia_groups.append( NG );
#
#Each group can be furthur split into bone matrix weighting (matrix palette splitting)
#
if( m_use_weights ):
len_va_weights = len(mesh.va_weights);
len_armamap = len( armatures_in_this_mesh.keys() );
if( (len_va_weights > 0) and (len_armamap > 0) ):
#Generate all matrix palettes from armature data
map_armature_name_to_palette = {};
for armax in armatures_in_this_mesh.keys():
intex = armatures_in_this_mesh[ armax ];
actualarmadata = BDATA_armatures[ intex ];
mp = {};
for bone in actualarmadata.bones :
mp[ bone.name ] = bone.index;
map_armature_name_to_palette[ armax ] = mp;
#
#Multiple-Armature weighting causes bone mapping to be invalid (because there are multiple choices if bones overlap)
#If the bone names don't overlap, this should work.
#
#
map_bone_name = {}; #Map bone name to [ armaturename, index ];
for armaname in map_armature_name_to_palette.keys():
for bonename in map_armature_name_to_palette[ armaname ].keys():
if( not bonename in map_bone_name ):
map_bone_name[ bonename ] = [ armaname, map_armature_name_to_palette[ armaname ][ bonename ] ];
else:
print "ERROR Duplicate bone; ignoring, taking from first armature.";
#Extend the array for this bone; so that ALL armatures per group are data-captured.
map_bone_name[ bonename ].extend[ armaname, map_armature_name_to_palette[ armaname ][ bonename ] ];
#
#Normalize weights (Only for those bones in our armatures)
#
wdi = 0;
wdi_max = len(mesh.va_weights);
while( wdi < wdi_max ):
wi = 0;
for wn in mesh.va_weights[wdi].weight_names:
if( wn in map_bone_name ):
mesh.va_weights[wdi].weight_indicies[wi] = map_bone_name[ wn ][1];
else:
print "#WARNING bone "+str(wn)+"not in armature; removing from weight value";
mesh.va_weights[wdi].weights[wi] = 0;
wi += 1;
ksum = 0.0;
for wv in mesh.va_weights[wdi].weights:
ksum += wv;
if( ksum > 0 ):
wvi = 0;
wvi_max = len( mesh.va_weights[wdi].weights );
while( wvi < wvi_max ):
mesh.va_weights[wdi].weights[wvi] /= ksum;
wvi += 1;
wdi += 1;
#
# Error check weight groups and build default palette maps?
#
max_matrices_in_this_mesh = 0;
map_meshgroup_to_bones = {};
for NG in mesh.ia_groups:
max_matrices_per_face = 0;
map_face_to_palette_depth = {};
map_face_to_bones = {};
map_bone_to_indicies = {};
all_bone_names = {};
for iaindex in NG.indicies:
IAF = mesh.ia_faces[ iaindex ];
facebones = {};
maxpal = 0;
for vaindex in IAF.indicies:
V = mesh.va_verts[ vaindex ];
if( (V.weight >= 0) and (V.weight < len_va_weights)):
W = mesh.va_weights[ V.weight ];
wcount = len( W.weight_names );
if( wcount > maxpal ):
maxpal = wcount;
for bonename in W.weight_names :
facebones[ bonename ] = vaindex;
if( not bonename in all_bone_names ):
all_bone_names[ bonename ] = []
all_bone_names[ bonename ].append( vaindex );
else:
print 'ERROR bad weight index; impossible: '+str(V.weight) +'/' +str(len_va_weights) +' : ' + str(VA_weight_curr_index) +' ' + str(VA_weight_max);
#Generate facebone map
for bonename in facebones:
if( not bonename in map_bone_to_indicies):
map_bone_to_indicies[ bonename ] = {};
map_bone_to_indicies[ bonename ][ iaindex ] = 0;
map_face_to_bones[ iaindex ] = facebones.keys();
map_face_to_palette_depth[ iaindex ] = maxpal;
if( maxpal > max_matrices_per_face ):
max_matrices_per_face = maxpal;
if( max_matrices_per_face > max_matrices_in_this_mesh ):
max_matrices_in_this_mesh = max_matrices_per_face;
if( max_matrices_per_face > max_matrices_in_this_mesh ):
max_matrices_in_this_mesh = max_matrices_per_face;
#Sort bones by depth then name (?) We could sort by # of influences, but it is likely that "shared" faces will cause huge problems
bonessortstruct = [];
for bname in all_bone_names.keys():
if( bname in map_bone_name ):
blinked = map_bone_name[ bname ];
#blinked[0] = armature name, blinked[1] = bone index
barmareal = BDATA_armatures[ BDATA_armatures_map[ blinked[0] ] ];
bdatareal = barmareal.bones[ blinked[1] ];
invcount = len(barmareal.bones) - bdatareal.child_count;
#Sort by depth, then by "number of children" (?) then by name?
bonessortstruct.append( [invcount, bname, blinked[1], blinked[0] ] );
bonessortstruct.sort( eSortByFirstThenSecondElement );
#print [ [v[0],v[1]] for v in bonessortstruct ];
map_meshgroup_to_bones[ NG.index ] = [ bonessortstruct, map_face_to_bones, map_bone_to_indicies ];
if( max_matrices_in_this_mesh > 4 ):
print '#WARNING - matrix data for '+mesh.name+' exceeds 4 indicies : ' + str(max_matrices_in_this_mesh);
#
#now we have:
#map_meshgroup_to_bones[ NG.index ] = [ array of bone data[ depth, name, index, armaname ], map_face[ iaindex ] = [bonenames] ]
#
allgroupsplits = {};
for NG in mesh.ia_groups:
newgroupsplits = [];
mesgmap = map_meshgroup_to_bones[ NG.index ];
bonesortstruct = mesgmap[0]; #array[ depth, name, armaturename, armatureindex ]
mapfaceiaindex = mesgmap[1]; ##for iaindex in NG.indicies: map_face_to_bones[ iaindex ] = facebones.keys(); #Names of bones per face (for splitting)
mapbonetofaceindicies = mesgmap[2]; #Maps bone name to map of face indicies used by it (all in this group)
#So need a consumption list; so we can remove faces if we have already used them.
remaining_faces = {};
for iaindex in NG.indicies:
remaining_faces[ iaindex ] = 0;
prev_bones = {};
curr_indicies = [];
for bonedata in bonesortstruct:
bonename = bonedata[1]; #Careful
indcopy = mapbonetofaceindicies[ bonename ].keys(); #Get all inidicies in this bone that we care about
for iaindex in indcopy:
if( iaindex in remaining_faces ): #Have we already removed it?
#IAF = mesh.ia_faces[ iaindex ]; #Not needed
IBPF = mapfaceiaindex[ iaindex ]; #Bone names in this face
#Copy current list
curr_bones = prev_bones;
curr_bones = {};
for oldkey in prev_bones.keys(): #Make a copy, for sure
curr_bones[ oldkey ] = 0;
#Add all new bones to this current list
for B in IBPF:
if B not in curr_bones:
curr_bones[ B ] = 0;
#Did we exceed the length ?
if( len( curr_bones.keys() ) > GLOBAL_MAX_SPLIT_BONES ):
#Generate new split
newgroupsplits.append( [ curr_indicies, prev_bones.keys() ] );
curr_indicies = [];
prev_bones = {};#Rebuild bone map
for B in IBPF:
if B not in prev_bones:
prev_bones[ B ] = 0;
else:
#continue, removing this face:
curr_indicies.append( iaindex );
del remaining_faces[ iaindex ];
#del mapbonetofaceindicies[ bonename ][ iaindex ]; #Remove from bone map as well... (?)
prev_bones = curr_bones;
if( len(curr_indicies) > 0 ):
#Last bone split exists; add it
newgroupsplits.append( [ curr_indicies, prev_bones.keys() ] );
curr_indicies = [];
prev_bones = {};
if( len( remaining_faces.keys() ) > 0 ):
#Boneless faces remaing, add as final split:
newgroupsplits.append( [ remaining_faces.keys(), [] ] );
#Save split tree
allgroupsplits[ NG.index ] = newgroupsplits;
#Split (copy) groups according to split tree (to match matrix palette split)
#
#Genreate NEW groups based on copies of old groups according to the splits requested.
new_group_curr = 0;
new_groups = [];
for ngindex in allgroupsplits.keys():
NGold = mesh.ia_groups[ ngindex ];
for ngsplit in allgroupsplits[ ngindex ]:
#ngsplit[0] = [face indicies];
#ngsplit[1] = [bone names, or empty];
NGnew = cMesh.cIAGroup();
NGnew.index = new_group_curr;
#"Copy"
NGnew.mat = NGold.mat;
NGnew._blender_material_name = NGold._blender_material_name;
NGnew.mode = NGold.mode;
NGnew.image_name = NGold.image_name;
NGnew.image_filename = NGold.image_filename;
NGnew.transp = NGold.transp;
NGnew.polytype = NGold.polytype;
NGnew.indicies = ngsplit[0];
NGnew.matrixpalette = [];
palindex = 0;
for mptex in ngsplit[1]:
#bonename = bonedata[1];
if( mptex in map_bone_name ):
NML = cMesh.cIAMatrixPaletteLink();
NML.index = palindex;
Bdata = map_bone_name[ mptex ];
NML.bone_name = mptex;
NML.arma_name = Bdata[ 0 ];
NML.bone_index = Bdata[ 1 ];
NGnew.matrixpalette.append( NML );
palindex += 1;
else:
print 'ERROR bone '+str(mptex)+'not in armature; removing as palette data!';
new_groups.append( NGnew );
new_group_curr += 1;
#Replace old with the new
mesh.ia_groups = new_groups;
#
#Now that we have generate all the new polygroups; and the matrix palettes;
#
#newweightsmap = {};
#newweights = [];
#newweightsmap_curr = 0;
#vertindreplaced = {};
#map_vindex_to_gw = {};
#regrouper = {};
newfaceverts = {};
groupfaceconv = {};
for NG in mesh.ia_groups:
#Build map of bone name -> matrix palette;
bonemap = {};
mp_index = 0;
for mpentry in NG.matrixpalette:
bonemap[ mpentry.bone_name ] = [ mpentry.arma_name, mp_index ];
mp_index += 1;
groupweightsmap = {};
groupweightsmap_curr = 0;
groupweights = [];
groupvertsmap = {};
groupvertsmap_curr = 0;
groupverts = [];
faceconvmap = {};
#fix all face links & new weights
for iaindex in NG.indicies:
#Get linkedface:
IAF = mesh.ia_faces[ iaindex ]; #cLinkFace
FNVM = [];
#For all verticies in linked face:
vlocalindex = 0;
for vindex in IAF.indicies:
V = mesh.va_verts[ vindex ];
wi = V.weight;
newwi = -1;
if( wi >= 0 ):
oldW = mesh.va_weights[ wi ];
#Convert to palette local weight;
newWEIGHT = cMesh.cAWeight();
for w in oldW.weights:
newWEIGHT.weights.append( w );
newWEIGHT.weight_indicies.append( -1 );
wi = 0;
for wn in oldW.weight_names:
newWEIGHT.weight_names.append( wn );
if( wn in bonemap ): #Get index from matrix palette
newWEIGHT.weight_indicies[wi] = bonemap[ wn ][1]; #Apply matrix palette indicies to weight values
else:
print 'ERROR Bone not in matrix palette! Impossible!';
newWEIGHT.weight_indicies[wi] = -1; #ERROR!
wi += 1;
#Rebuild weight indexing (for this group)
newWEIGHTKey = newWEIGHT.get_key();
if( not newWEIGHTKey in groupweightsmap ):
newWEIGHT.index = groupweightsmap_curr;
groupweights.append( newWEIGHT );
groupweightsmap[ newWEIGHTKey ] = groupweightsmap_curr;
groupweightsmap_curr += 1;
else:
newWEIGHT = groupweights[ groupweightsmap[ newWEIGHTKey ] ];
newwi = newWEIGHT.index;
else:
print "Unweighted vertex: " + str( vindex );
#Create new face vertex
nVAV = cMesh.cLinkVertex();
nVAV.uv = V.uv;
nVAV.co = V.co;
nVAV.no = V.no;
nVAV.tangent = V.tangent;
nVAV.binormal = V.binormal;
nVAV.color = V.color;
nVAV.weight = newwi; #Weight is NEW weight index.
nVAV.index = -1;
#Does this face vertex already exist?
nVAVKey = nVAV.get_key();
if( not nVAVKey in groupvertsmap ):
nVAV.index = groupvertsmap_curr;
groupverts.append( nVAV );
groupvertsmap[ nVAVKey ] = groupvertsmap_curr;
groupvertsmap_curr += 1;
else:
nVAV = groupverts[ groupvertsmap[ nVAVKey ] ];
FNVM.append( nVAV.index ); #Rebuild faces with new data
#Repair face linkage:
#mesh.ia_faces[ iaindex ].indicies[ vlocalindex ] = nVAV.index;
#Add new va vertex
#mesh.va_verts.append( nVAV );
#Add to array of new face (weight) mappings
#FNVM.append( newWEIGHT.index );
vlocalindex += 1;
faceconvmap[ iaindex ] = FNVM;
groupfaceconv[ NG.index ] = [ groupverts, groupweights, faceconvmap ];
#
#Now rebuild the mesh face verts; and mesh faces per group, and the weights.
#
#groupfaceconv[ NG.index ] = [ groupverts, groupweights, faceconvmap ];
#
mesh.va_verts = [];
mesh.va_weights = [];
NG_index = 0;
NG_index_max = len( mesh.ia_groups );
while( NG_index < NG_index_max ):
gvconv = groupfaceconv[ NG_index ];
#Repair all arrays:
offset_va_verts = len( mesh.va_verts );
mesh.va_verts.extend( gvconv[0] );
offset_va_weights = len( mesh.va_weights );
mesh.va_weights.extend( gvconv[1] );
#Correct weight indicies inside of verticies as well:
videx = offset_va_verts;
videx_max = len(mesh.va_verts);
while( videx < videx_max ):
if( mesh.va_verts[ videx ].weight >= 0 ):
mesh.va_verts[ videx ].weight += offset_va_weights;
videx += 1;
#Rebuild faces:
faceconvmap = gvconv[2];
for fdex in faceconvmap.keys():
#Correct indicies to be non-local to group
cexverts = faceconvmap[ fdex ];
cindex = 0;
for vid in cexverts:
cexverts[ cindex ] = vid + offset_va_verts;
cindex += 1;
#apply!
mesh.ia_faces[ fdex ].indicies = cexverts;
#Now what?
mesh.ia_groups[ NG_index ].start_va_index = offset_va_verts;
mesh.ia_groups[ NG_index ].end_va_index = len( mesh.va_verts );
NG_index += 1;
#
#Each group can be furthur split into triangle strips/quad strips (face type sorting is already done)
#
# #ERROR - IA triangle stripping (allow degenerates?)
#
#After triangle stripping; we can optimize the VA to be inline/inorder with the triangle strips
#
# #ERROR - VA reordering (optimize)
#
#This is fairly tricky; Mainly because:
#
# 1. faces are best optimized by keeping the VA inline with teh faces; IE small jumps with reordered verticies.
# 2. faces must be put into a minimum number of triangle/quad strips with maximum length; minimizing splits is correct.
# 3. faces must be split by shader, texture, bone palette; ect.
#
#
#Specialized mesh stuff (Non-face meshes; which are just lines, usually collision boxes or lazers)
#
if( len(bMesh.faces) == 0 ):
vi = 0;
for v in bMesh.verts:
#New Coordinate
newCO = cMesh.cAVertex();
newCO.x = v.co[0];
newCO.y = v.co[1];
newCO.z = v.co[2];
#New Normal
newNO = cMesh.cANormal();
newNO.nx = v.no[0];
newNO.ny = v.no[1];
newNO.nz = v.no[2];
mesh.va_coords.append( newCO );
mesh.va_normals.append( newNO );
newVAV = cMesh.cLinkVertex();
newVAV.index = vi;
newVAV.co = vi;
newVAV.no = vi;
newVAV.tangent = -1;
newVAV.binormal = -1;
newVAV.uv = -1;
newVAV.color = -1;
newVAV.weight = -1;
mesh.va_verts.append( newVAV );
vi += 1;
#
#Save it!
#
mesh.index = BDATA_meshes_curr;
BDATA_meshes.append( mesh );
BDATA_meshes_map[ mesh.name ] = BDATA_meshes_curr;
BDATA_meshes_curr += 1;
obj.setLocation( OFFSET_LOCATION );
obj.setEuler( OFFSET_EULER );
obj.setSize( OFFSET_SCALE );
#
#Next, resort all VA verticies to minimize dependancy; put the clost together single face verticies first, and duals last.
#
# [face 0 2 3 ][ 1 4 7 ] => [0 1 2][3 4 5]
# [ 0a 1b 2c 3d 4e 5f 6g 7h ] => [0a 1c 2d 3b 4e 5h 6f 7g
#
#Now that the faces and VA verticies are minimized (most optimal for sending to card)
#Sort face into polygroups based on material, texture assigned, face mode, transparency.
#
#
#Now that face groups are broken up correctly, test for matrix palette compatibility.
# -> This is exceedingly difficult, since for 1 mesh it can have multiple armatures;
# -> It also requires a matrix map, that converts armature index/bone to local palette index.
# Maybe this isn't so bad.
# Then generate matrix palette data and split up groups as needed.
#
#Finally, split up groups into face types (triangles, quads)
#
#
#And then, the final blow; convert each polygroup into strips; either degenerate or not.
# -> Optionally convert quads into triangles first (they will still be optimal)
# -> You can only stripify triangles or quads.
# -> Degenerates will save mode changes; but possibly annoy your graphics card.
#
#
#Now you have a mesh that is optimally sorted, split by mode change, stripped, and palettized.
#
#
#Now we go back through materials found in the mesh? Hm.
#
#Materials?
for mname in MAT_allnames.keys():
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
bMat = Blender.Material.Get( mname );
if( bMat != None ):
newmat = cMaterial();
newmat.name = mname;
#
#We should be able to generate GLSL code from each material shader type;
#Internally, blender does this automatically, so, hm.
#
#
if( Blender.World.GetCurrent() != None ): #Weird, but it can happen.
ambientcolor = Blender.World.GetCurrent().getAmb();
else:
ambientcolor = [0,0,0];
#
newmat.color_ambient[0] = bMat.amb*ambientcolor[0]; #Hm. This should combine diffuse*(1-ref) ?
newmat.color_ambient[1] = bMat.amb*ambientcolor[1];
newmat.color_ambient[2] = bMat.amb*ambientcolor[2];
#
newmat.color_diffuse[0] = bMat.ref*bMat.rgbCol[0]; #bMat.ref = reflectivity... Hm.
newmat.color_diffuse[1] = bMat.ref*bMat.rgbCol[1];
newmat.color_diffuse[2] = bMat.ref*bMat.rgbCol[2];
#
newmat.color_specular[0] = bMat.specCol[0];
newmat.color_specular[1] = bMat.specCol[1];
newmat.color_specular[2] = bMat.specCol[2];
newmat.color_specular[3] = bMat.specTransp; #Specular transparency?
#
newmat.color_emissive[0] = bMat.emit*bMat.rgbCol[0];
newmat.color_emissive[1] = bMat.emit*bMat.rgbCol[1];
newmat.color_emissive[2] = bMat.emit*bMat.rgbCol[2];
newmat.color_emissive[3] = 1.0;
#
newmat.specularity = bMat.hard; #bMat.spec = degree of specularity
newmat.transparency = bMat.alpha;
#
newmat.color_diffuse[3] = newmat.transparency;
newmat.color_ambient[3] = newmat.transparency;
#
newmat.textures = [];
#
#Save image name from material, in linear order (combiner)
# #ERROR, need to save the combination mode (mix, alpha, decal...)
#
alltexes = bMat.getTextures();
for mtex in alltexes:
if( mtex != None ):
if( mtex.tex != None ):
if( mtex.tex.image != None ):
#if mtex.tex.type == Blender.Texture.Types.IMAGE:
newmat.textures.append( mtex.tex.image.getName() );
#TEX_names[ mtex.tex.image.getName() ] = mtex.tex.image.getFilename();
newmat.index = BDATA_materials_curr;
BDATA_materials.append( newmat );
BDATA_materials_map[ newmat.name ] = BDATA_materials_curr;
BDATA_materials_curr += 1;
#Textures?
for tname in all_TEX_names.keys():
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
newtex = cTexture();
newtex.name = tname;
newtex.filename = all_TEX_names[ tname ];
newtex.data = [];
newtex.palette = [];
newtex.dimensions = [0,0,0];
newtex.type = "RGBA";
newtex.bpp = 32;
newtex.epp = 4;
newtex.hexdata = [];
T = Blender.Image.Get( newtex.name );
if( T != None ):
#Get some values:
try:
newtex.bpp = T.getDepth();
except:
newtex.bpp = 0;
print "ERROR Missing Image: " + newtex.name;
if( newtex.bpp > 0 ):
if( newtex.bpp == 32 ):
newtex.epp = 4;
newtex.type = "RGBA";
elif( newtex.bpp == 24 ):
newtex.epp = 3;
newtex.type = "RGB";
elif( newtex.bpp == 8 ):
newtex.epp = 1;
newtex.type = "INTENSITY";
else:
newtex.epp = newtex.bpp / 4;
print "Error, texture "+newtex.name+" with odd bpp: %d" % newtex.bpp;
#
dims = T.getSize();
newtex.dimensions[0] = dims[0];
newtex.dimensions[1] = dims[1];
if( T.has_data or T.packed ):
#
#Create image in memory (do a color count until we hit 255 colors? hm.)
#
# What about normal maps? And converting XYZ normals into IA normals??
#
maxintensitychange = 0;
maxalphachange = 0;
maxdeltanormal = 1.0;
mindeltanormal = 1.0;
imgdata = [];
consta = 255;
y = 0;
while( y < newtex.dimensions[1] ):
x = 0;
while( x < newtex.dimensions[0] ):
rgba = T.getPixelI(x, y);
consta &= rgba[3];
#Detect greyscale images
da = abs(rgba[0] - rgba[1]);
db = abs(rgba[2] - rgba[1]);
if( da > maxintensitychange ):
maxintensitychange = da;
if( db > maxintensitychange ):
maxintensitychange = db;
#Detect (dot3) normal map
nrgba = T.getPixelF(x, y);
nrgba[0] -= 0.5;
nrgba[1] -= 0.5;
nrgba[2] -= 0.5;
nrgba[0] *= 2.0; #-1.0...1.0
nrgba[1] *= 2.0;
nrgba[2] *= 2.0;
dd = (nrgba[0]*nrgba[0] + nrgba[1]*nrgba[1] + nrgba[2]*nrgba[2]); #hmrgh.
if( dd > maxdeltanormal ):
maxdeltanormal = dd;
if( dd < mindeltanormal ):
mindeltanormal = dd;
#Add RGBA data by default
imgdata.append( [rgba[0], rgba[1], rgba[2], rgba[3]] );
x += 1;
y += 1;
#Can we detect normal maps and alpha only maps? (hm)
if( maxintensitychange <= 0 ):
#Are all colors the same? Hm.
#All colors are greyscale;
if( consta == 255 ):
#And all alpha values are constant. Intensity or Alpha then.
newtex.epp = 1;
newtex.bpp = 8;
newtex.type = "INTENSITY";
for elm in imgdata:
newtex.data.extend( [elm[0]] );
else:
#all greyscale colors AND alpha values? Intensity + Alpha then.
newtex.epp = 2;
newtex.bpp = 16;
newtex.type = "INTENSITYALPHA";
for elm in imgdata:
newtex.data.extend( [elm[0],elm[3]] );
else:
#Colors change too much to be a alpha/intensity map.
delnorm = (maxdeltanormal - mindeltanormal);
if( delnorm < (2.0 / 255.0) ):
if( consta == 255 ):
#All pixels obey normal vector contraints; how strange. Let's assume it;s a normal map?
newtex.epp = 3;
newtex.bpp = 24;
newtex.type = "NORMAL";
for elm in imgdata:
newtex.data.extend( [elm[0], elm[1], elm[2]] );
else:
#Image obeys vector rule but has an alpha channel...
#
#this image should be split into a NORMALIA and a INTENSITY image.
#
newtex.epp = 4;
newtex.bpp = 32;
newtex.type = "NORMALINTENSITY";
for elm in imgdata:
newtex.data.extend( [elm[0], elm[1], elm[2], elm[3]] );
else:
if( consta == 255 ):
#Image has constant 255 alpha channel, and is thus RGB not RGBA
#use_alpha = False;
newtex.epp = 3;
newtex.bpp = 24;
newtex.type = "RGB";
for elm in imgdata:
newtex.data.extend( [elm[0], elm[1], elm[2]] );
else:
#Image does Not have constant alpha channel OR RGB channel.
newtex.epp = 4;
newtex.bpp = 32;
newtex.type = "RGBA";
for elm in imgdata:
newtex.data.extend( [elm[0], elm[1], elm[2], elm[3]] );
newtex.index = BDATA_textures_curr;
BDATA_textures.append( newtex );
BDATA_textures_map[ newtex.name ] = BDATA_textures_curr;
BDATA_textures_curr += 1;
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_lamp_objects) + 1;
progress_curr = 0;
for obj in blender_selected_lamp_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Lamp: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Convert all lamp values into properties, export as a generic object
pass;
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_camera_objects) + 1;
progress_curr = 0;
for obj in blender_selected_camera_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Camera: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Convert all camera values into properties, export as a generic object
pass;
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_surface_objects) + 1;
progress_curr = 0;
for obj in blender_selected_surface_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Surface: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Ignore surfaces. Possibly convert to meshes and export?
pass;
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_metaball_objects) + 1;
progress_curr = 0;
for obj in blender_selected_metaball_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Metaball: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Ignore metaballs. Possibly convert to meshes and export?
#Or convert to object lists per meta object and export properties?
pass;
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_text_objects) + 1;
progress_curr = 0;
for obj in blender_selected_text_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Text: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Ignore text objects. Possibly convert to meshes and export?
pass;
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(blender_selected_empty_objects) + 1;
progress_curr = 0;
for obj in blender_selected_empty_objects:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Empty: " + obj.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
#Convert all object properties into properties, and export object
#also generate physics properties and other things to properties
pass;
#
#Well, this is bizzare. Not sure what to do here... All selected objects, or ALL SCENES? hm.
#
all_scenes = Blender.Scene.Get();
progress_start = 0.0;
progress_end = 1.0;
progress_count = len(all_scenes) + 1;
progress_curr = 0;
for bscene in all_scenes:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Scenes: " + bscene.name, GLOBAL_DEBUG_SHOW_PROGRESS );
progress_curr += 1;
scened = cScene();
#Resort objects by parenting order (root objects first, deepest objects last)
resorted_objects = [];
maxdepth = len( bscene.objects ); #Avoid circular stuff
for obj in bscene.objects:
if( obj != None ):
D = 0;
C = obj;
X = 0;
while C.getParent() != None: #blender enforces non-looped trees; this is safe
C = C.getParent();
D += 1;
X += 1;
if( X > maxdepth ):
break;
resorted_objects.append([D, obj]);
#
#Create a sorted array of bone keys, in order from least to greatest depth
#
def unsortedcompare( A, B ):
if( A[0] > B[0] ):
return 1;
elif( A[0] == B[0] ):
if( A[1].name > B[1].name ):
return 1;
elif( A[1].name == B[1].name ):
return 0;
else:
return -1;
else:
return -1;
#
resorted_objects.sort( unsortedcompare );
BDATA_objects_map = {};
for objd in resorted_objects:
obj_depth = objd[0];
obj = objd[1]
#Every object loaded is an object, and can be converted to a scene object.
#Pull out properties, link to data objects (now actually in the file) and go from there.
nobj = cObject();
nobj.name = obj.name;
nobj.pos = [ obj.loc[0], obj.loc[1], obj.loc[2] ];
#nobj.quat = [0,0,0,0]; #Quaternion AKA Orientation
#nobj.matrix = [1,0,0,0,1,0,0,0,1]; #Matrix orientation
#nobj.scale = [1,1,1]; #scale factor (local)
nobj.parent = None;
if( obj.getParent() != None ):
if( obj.parentType == Blender.Object.ParentTypes.CURVE ):
#Possible Spline point?
pass;
elif( obj.parentType == Blender.Object.ParentTypes.LATTICE ):
#Parented to lattice? huh.
pass;
elif( obj.parentType == Blender.Object.ParentTypes.VERT1 ):
#Vertex hook? Spline Point again?
pass;
elif( obj.parentType == Blender.Object.ParentTypes.VERT3 ):
#Not sure what "verts" are as opposed to "vert"
pass;
elif( obj.parentType == Blender.Object.ParentTypes.ARMATURE ):
#This doesn't mean anything anymore
pass;
elif( obj.parentType == Blender.Object.ParentTypes.BONE ):
#This implies a spline point?
pass;
if( obj.getParent().name in BDATA_objects_map ):
idex = BDATA_objects_map[ obj.getParent().name ];
nobj.parent = scened.objects[ idex ].name;
#ERROR ERROR space conversions?
matrix = obj.getMatrix('worldspace');
row0 = matrix[0];
row1 = matrix[1];
row2 = matrix[2];
row3 = matrix[3];
nobj.pos = [ row3[0], row3[1], row3[2] ];
nobj.matrix = [ row0[0], row0[1], row0[2], row1[0], row1[1], row1[2], row2[0], row2[1], row2[2] ]; #Matrix orientation
#nobj.matrix = [ row0[0], row1[0], row2[0], row0[1], row1[1], row2[1], row0[2], row1[2], row2[2] ]; #Matrix orientation
worldscale = obj.getSize('worldspace'); #Because we don;t do world scaling, we might have to use this instead of local
nobj.scale = [ worldscale[0], worldscale[1], worldscale[2] ]; #scale factor (local);
nobj.matrix = eMatrixNormalize( nobj.matrix );
#also check for constraints that do this?
#Have to check constraints for this:
nobj.parent_bone = None;
nobj.depth = obj_depth;
#ERROR #ERROR #ERROR
#Data linkage? Hm. Can link this to many things, not just 1
#Read in game properties (int, float, string) which are very useful!
for prop in obj.getAllProperties():
ty = prop.getType();
newprop = cObject.cAProp();
newprop.btype = ty;
newprop.name = prop.getName();
if( ty == 'TIME' or ty == 'FLOAT' ):
newprop.type = "float";
newprop.data = ("%f" % (prop.getData()));
elif( ty == 'INT' or ty == 'BOOL' ):
newprop.type = "int";
newprop.data = ("%d" % (prop.getData()));
else:
newprop.type = "string";
newprop.data = prop.getData();
nobj.properties.append( newprop );
#
#Physics & Shape properties!
#
#phys = gPhysics();
#phys.name = blobj.name;
#
#aabb data is stored as 8 points (the 8 corners of the box)
try:
local_aabb = obj.getBoundBox(0); #Localspace bounding box (Collision Shape Bounding Box)
global_aabb = obj.getBoundBox(1); #Worldspace bounding box (AABB)
except:
local_aabb = None;
global_aabb = None;
if( (local_aabb != None) and (global_aabb != None) ):
local_min_aabb = local_aabb[0];
local_max_aabb = local_aabb[0];
for p in local_aabb:
if( p[0] < local_min_aabb[0] ): local_min_aabb[0] = p[0];
if( p[1] < local_min_aabb[1] ): local_min_aabb[1] = p[1];
if( p[2] < local_min_aabb[2] ): local_min_aabb[2] = p[2];
if( p[0] > local_max_aabb[0] ): local_max_aabb[0] = p[0];
if( p[1] > local_max_aabb[1] ): local_max_aabb[1] = p[1];
if( p[2] > local_max_aabb[2] ): local_max_aabb[2] = p[2];
global_min_aabb = global_aabb[0];
global_max_aabb = global_aabb[0];
for p in global_aabb:
if( p[0] < global_min_aabb[0] ): global_min_aabb[0] = p[0];
if( p[1] < global_min_aabb[1] ): global_min_aabb[1] = p[1];
if( p[2] < global_min_aabb[2] ): global_min_aabb[2] = p[2];
if( p[0] > global_max_aabb[0] ): global_max_aabb[0] = p[0];
if( p[1] > global_max_aabb[1] ): global_max_aabb[1] = p[1];
if( p[2] > global_max_aabb[2] ): global_max_aabb[2] = p[2];
#Save *global* bounding box data anyways? (always helpful information! especially for static things.)
#mass = obj.rbMass;
#radius = obj.rbRadius;
bFlags = obj.rbFlags;
bShape = obj.rbShapeBoundType;
#
if( bFlags != None ):
#
#phys.actor = obj.bFlags & Object.RBFlags.ACTOR;
#phys.dynamic = obj.bFlags & Object.RBFlags['DYNAMIC'];
#phys.solid = not (obj.bFlags & Object.RBFlags['GHOST']);
#phys.rotational = obj.bFlags & Object.RBFlags['RIGIDBODY'];
#phys.persistant = obj.bFlags & Object.RBFlags['COLLISION_RESPONSE'];
#phys.friction_surface = obj.bFlags & Object.RBFlags['USEFH'];
#phys.friction_rotation = obj.bFlags & Object.RBFlags['ROTFH'];
#phys.friction_ansiotropic = obj.bFlags & Object.RBFlags['ANISOTROPIC'];
pass;
#
#phys.cshape = "";
if( bShape != None ):
#
cshape = "";
if( bShape == Blender.Object.RBShapes.BOX ):
cshape = "box";
#Convert "box" definition & parameters to geo.h definitions
#Convert local_aabb into a box. Easy enough (min/max)
elif( bShape == Blender.Object.RBShapes.SPHERE ):
cshape = "sphere";
#Convert "sphere" definition & parameters to geo.h definitions
#Convert local_aabb into an aabb ellipse or sphere or axis-ellipse?. Easy enough (min/max)
elif( bShape == Blender.Object.RBShapes.CYLINDER ):
cshape = "cylinder";
#Convert "cylinder" definition & parameters to geo.h definitions
#convert local_aabb into a cylinder as per blender definitions (f***)
elif( bShape == Blender.Object.RBShapes.CONE ):
cshape = "cone";
#Convert "cone" definition & parameters to geo.h definitions
#convert local_aabb into a cone as per blender definitions (f***)
elif( bShape == Blender.Object.RBShapes.POLYHEDERON ):
#
#! This covers static poly-meshes and polyhedral hulls! #ERROR
#
cshape = "poly";
#
#Yipes. I guess we can't really do much here; This is a "static triangle mesh" collision shape.
#Is it terrain? is is polysoup? What can we do here?
#
nobj.links = [];
newLink = cObject.cALink();
newLink.name = "";
newLink.btype = obj.type;
newLink.type = "";
if( obj.type == 'Curve' ):
pass;
elif( obj.type == 'Armature' ):
newLink.type = "armature";
newLink.name = obj.getData(1,0); # #ERROR, might have to convert names?
pass;
elif( obj.type == 'Mesh' ):
newLink.type = "mesh";
newLink.name = obj.getData(1,0); # #ERROR, might have to convert names?
pass;
elif( obj.type == 'Lamp' ):
pass;
elif( obj.type == 'Camera' ):
pass;
elif( obj.type == 'Surface' ):
pass;
elif( obj.type == 'Metaball' ):
pass;
elif( obj.type == 'Text' ):
pass;
else:
pass;
if( len( newLink.type ) > 0 ):
nobj.links.append( newLink );
#Link the data from the type of object to this object; including other things like:
# Mesh, Mesh + Armature, Mesh + Deformers
# Armature, Armature + Spline
# ?
"""
#
#Create a node for export
#
node = gNode();
node.name = blobj.name;
node.data = blobj.getData(1,0);
node.data_type = blobj.getType();
#
if( node.data == None ):
#
#Check for collision types/geometry linkages? (specs)
#
node.data = "";
#
matrix = blobj.getMatrix('localspace').copy();
node.pos = matrix[3][0:3]; #Correct position, relative to parent (but only the OBJECT parent)
Q = ConvertMatrixToQuaternion( matrix, False );
node.quat = [Q.x,Q.y,Q.z,Q.w];
node.scale = blobj.getSize();
if blobj.getParent() == None:
node.parent = "";
else:
node.parent = blobj.getParent().name;
#
#Bone parent option? (important for phys/shapes)
#
if( blobj.parentbonename != None ):
#
if( blobj.parentType == Object.ParentTypes.BONE ):
pass;
#
node.parent_bone = blobj.parentbonename;
#
#Correct transformation to actual parent bone
#
arm_mat = blobj.getParent().matrix; #(worldspace)
#
bArma = blobj.getParent().getData(0,0);
bone_mat = bArma.bones[node.parent_bone].matrix['ARMATURESPACE'];
#
#Get the current bone position
#
cpose = blobj.getParent().getPose();
posemat = None;
if( cpose != None ):
#
if( node.parent_bone in cpose.bones.keys() ):
#
bone_mat = cpose.bones[node.parent_bone].poseMatrix;
#
#Now, make the node matrix a local matrix (to compare with bone)
#
node_invert = blobj.getMatrix('localspace').copy();
#
#Fix armature errors (non uniform matricies, non XYZ aligned)
#
multcpy2 = bone_mat.copy();
multme2 = Mathutils.RotationMatrix(90, 4, 'z');
multcpy2 = multme2 * multcpy2;
bone_mat = multcpy2;
#
bone_mat.invert();
localfinal = node_invert * bone_mat; #This should be the local offset from the bone
#
Q = ConvertMatrixToQuaternion( localfinal, False );
#
#Save changes (scaling does not apply? #ERROR)
#
node.pos = localfinal[3][0:3];
node.quat = [Q.x,Q.y,Q.z,Q.w];
#
"""
#nobj.properties = [];
nobj.index = len(scened.objects);
BDATA_objects_map[ nobj.name ] = nobj.index;
scened.objects.append( nobj );
scened.index = BDATA_scenes_curr;
BDATA_scenes_map[ scened.name ] = BDATA_scenes_curr;
BDATA_scenes.append( scened );
BDATA_scenes_curr += 1;
#
#Time to correct ALL coordinate systems:
#
#GLOBAL_TRANSMATRIX
#GLOBAL_TRANSMATRIX_BONE
#eConvertCoordinatesVector( value, GLOBAL_TRANSMATRIX )
#eConvertQuatToMatrix( value, GLOBAL_TRANSMATRIX )
#eConvertCoordinatesQuaternion( value, GLOBAL_TRANSMATRIX )
#eConvertCoordinatesScale( value, GLOBAL_TRANSMATRIX )
#eConvertCoordinatesMatrix( value, GLOBAL_TRANSMATRIX )
eDisplayProgressBarOption( 0.5, "CoordConv...", GLOBAL_DEBUG_SHOW_PROGRESS );
A_index = 0;
for arma in BDATA_armatures:
for bone in arma.bones:
#Convert all bones to defined coordinate system
bone.pos = eConvertCoordinatesVector( bone.pos, GLOBAL_TRANSMATRIX );
bone.matrix = eConvertCoordinatesMatrix( bone.matrix, GLOBAL_TRANSMATRIX_BONE );
for ikchain in arma.ikchains:
# ???
pass;
for splinepoint in arma.splinepoints:
# ???
pass;
A_index += 1;
C_index = 0;
for channa in BDATA_channels:
V_index = 0;
for kpos in channa.key_positions:
#Vout = eConvertCoordinatesVector( [kpos.x,kpos.y,kpos.z], GLOBAL_TRANSMATRIX_BONE );
#kpos.x = Vout[0];
#kpos.y = Vout[1];
#kpos.z = Vout[2];
V_index += 1;
V_index = 0;
for kquat in channa.key_quats:
#xquat = eConvertCoordinatesQuaternion( [kquat.x, kquat.y, kquat.z, kquat.w], GLOBAL_TRANSMATRIX_BONE );
#kquat.x = xquat[0];
#kquat.y = xquat[1];
#kquat.z = xquat[2];
#kquat.w = xquat[3];
V_index += 1;
V_index = 0;
for kmat in channa.key_matrices:
#R = eConvertCoordinatesMatrix( kmat, GLOBAL_TRANSMATRIX_BONE );
V_index += 1;
V_index = 0;
for kscale in channa.key_scales:
#Vout = eConvertCoordinatesScale( [kscale.x,kscale.y,kscale.z], GLOBAL_TRANSMATRIX_BONE );
#kscale.x = Vout[0];
#kscale.y = Vout[1];
#kscale.z = Vout[2];
V_index += 1;
C_index += 1;
#for channa BDATA_animations; #No transformation data present.
M_index = 0;
for meshy in BDATA_meshes:
V_index = 0;
for V in meshy.va_coords:
Vout = eConvertCoordinatesVector( [V.x,V.y,V.z], GLOBAL_TRANSMATRIX );
V.x = Vout[0];
V.y = Vout[1];
V.z = Vout[2];
V_index += 1;
V_index = 0;
for V in meshy.va_normals:
Vout = eConvertCoordinatesVector( [V.nx,V.ny,V.nz], GLOBAL_TRANSMATRIX );
V.nx = Vout[0];
V.ny = Vout[1];
V.nz = Vout[2];
V_index += 1;
V_index = 0;
for V in meshy.va_tangents:
Vout = eConvertCoordinatesVector( [V.tx,V.ty,V.tz], GLOBAL_TRANSMATRIX );
V.tx = Vout[0];
V.ty = Vout[1];
V.tz = Vout[2];
V_index += 1;
M_index += 1;
#for texy in BDATA_textures: #No transformation data present.
#global BDATA_materials; #No transformation data present.
for modelo in BDATA_models:
#?
pass;
S_index = 0;
for sceno in BDATA_scenes:
O_index = 0;
for objo in sceno.objects:
objo.pos = eConvertCoordinatesVector( objo.pos, GLOBAL_TRANSMATRIX );
objo.quat = eConvertCoordinatesQuaternion( objo.quat, GLOBAL_TRANSMATRIX );
objo.matrix = eConvertCoordinatesMatrix( objo.matrix, GLOBAL_TRANSMATRIX );
objo.scale = eConvertCoordinatesScale( objo.scale, GLOBAL_TRANSMATRIX );
O_index += 1;
S_index += 1;
#####################################################################
#
#The export function
#
def TEDexport( txt, log ):
debug_time_init = eGetTime();
debug_time_date = eGetDate();
#eLog( "Started...", log );
progress_start = 0.0;
progress_end = 0.1;
#
#Export code here
#
#----------------------------------------------------------------------
global GLOBAL_FILE_NAME;
global GLOBAL_DATAGROUP_NAME;
global GLOBAL_TRANSMATRIX;
global GLOBAL_TRANSMATRIX_BONE;
global GLOBAL_DEBUG_SHOW_PROGRESS;
global GLOBAL_QUIT;
eDisplayProgressBar( 0.0, "Converting..." );
global BDATA_armatures;
global BDATA_channels;
global BDATA_animations;
global BDATA_meshes;
global BDATA_textures;
global BDATA_materials;
global BDATA_models;
global BDATA_scenes;
#
#Convert blender data into custom classes in memory
#
eConvertBlender();
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
if( GLOBAL_QUIT ): return;
#
#Convert generic data to custom format?
#
eDisplayProgressBar( 0.75, "Writing..." );
#
#Start writing out the file
#
XMF = dxmlwriter();
#XMF.write( "<?xml version=\"DXML\"?>\n" );
XMF.push( "ted" );
XMF.writeline( "name", GLOBAL_FILE_NAME );
XMF.writeline( "date", debug_time_date );
XMF.writeline( "author", __version__ );
#Save location, save 16 byte hex UID
xml_file_uid_start = XMF.getsize();
xml_file_uid_depth = XMF.getdepth();
XMF.writeline( "checksum", "00000000000000000000000000000000" );
xml_file_uid_end = XMF.getsize();
datagroupname = GLOBAL_DATAGROUP_NAME;
#try:
# datagroupname = Blender.Scene.GetCurrent().getName();
#except:
# datagroupname = GLOBAL_DATAGROUP_NAME;
XMF.writeline( "datagroup", datagroupname ); #Scene name? Hm.
#
#note that you still need the coordinate conversion routines to work here.
#
#GLOBAL_TRANSMATRIX; #For everything in object space
#GLOBAL_TRANSMATRIX_BONE; #For everything in bone space
progress_end = 1.0;
progress_start = 0.0;
progress_count = 100;
progress_curr = 10;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out armatures
XMF.setprecision( 4 );
carma_len = len( BDATA_armatures );
if( carma_len > 0 ):
XMF.push( "armatures" );
XMF.writeline( "count", carma_len );
for carma in BDATA_armatures:
XMF.push( "armature" );
XMF.writeline( "name", carma.name );
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
bone_len = len(carma.bones);
if( bone_len > 0 ):
XMF.push( "bones" );
XMF.writeline( "count", bone_len );
for bone in carma.bones:
XMF.push( "bone" );
XMF.writeline( "name", bone.name );
if( bone.parent != None ):
XMF.writeline( "parent", bone.parent );
XMF.writeline( "position", bone.pos );
#No quaternion? Hm. good!
XMF.push("matrix");
XMF.writearraydata( bone.matrix, 3 );
XMF.pop("matrix");
XMF.writeline( "radius_start", bone.radius_start );
XMF.writeline( "radius_end", bone.radius_end );
XMF.writeline( "length", bone.length );
XMF.writeline( "depth", bone.depth );
XMF.writeline( "scalemode", bone.scalemode );
XMF.pop( "bone" );
XMF.pop( "bones" );
#IK Chains (and IK limits)
len_iks = len( carma.ikchains );
if( len_iks > 0 ):
XMF.push( "ik_chains" );
XMF.writeline( "count", len_iks );
for ikdata in carma.ikchains:
write_depth = 0;
write_poslim = 0;
write_rotlim = 0;
if( ikdata.depth > 0 ):
write_depth = 1;
if( (ikdata.iklim_pos_min[0] < ikdata.iklim_pos_max[0]) or \
(ikdata.iklim_pos_min[1] < ikdata.iklim_pos_max[1]) or \
(ikdata.iklim_pos_min[2] < ikdata.iklim_pos_max[2]) ):
write_poslim = 1;
if( (ikdata.iklim_rot_min[0] < ikdata.iklim_rot_max[0]) or \
(ikdata.iklim_rot_min[1] < ikdata.iklim_rot_max[1]) or \
(ikdata.iklim_rot_min[2] < ikdata.iklim_rot_max[2]) ):
write_rotlim = 1;
if( write_depth or write_poslim or write_rotlim ):
XMF.push( "ik_chain" );
XMF.writeline( "name", ikdata.name );
XMF.writeline( "bone", ikdata.startbone );
if write_depth:
XMF.writeline( "depth", ikdata.depth );
if write_poslim:
pvec = [ ikdata.iklim_pos_min[0], ikdata.iklim_pos_min[1], ikdata.iklim_pos_min[2], ikdata.iklim_pos_max[0], ikdata.iklim_pos_max[1], ikdata.iklim_pos_max[2], ikdata.iklim_pos_stiff[0], ikdata.iklim_pos_stiff[1], ikdata.iklim_pos_stiff[2] ];
XMF.writeline( "position", pvec );
if write_rotlim:
pvec = [ ikdata.iklim_rot_min[0], ikdata.iklim_rot_min[1], ikdata.iklim_rot_min[2], ikdata.iklim_rot_max[0], ikdata.iklim_rot_max[1], ikdata.iklim_rot_max[2], ikdata.iklim_rot_stiff[0], ikdata.iklim_rot_stiff[1], ikdata.iklim_rot_stiff[2] ];
XMF.writeline( "rotation", pvec );
XMF.pop( "ik_chain" );
XMF.pop( "ik_chains" );
#Spline points
len_splinepoints = len( carma.splinepoints );
if( len_splinepoints > 0 ):
XMF.push( "splinepoints" );
XMF.writeline( "count", len_splinepoints );
for sp in carma.splinepoints:
XMF.push( "splinepoint" );
XMF.writeline( "name", sp.name );
XMF.writeline( "bone", sp.bone );
XMF.writeline( "position", [ sp.x, sp.y, sp.z ] );
XMF.writeline( "direction", [ sp.vx, sp.vy, sp.vz ] );
XMF.pop( "splinepoint" );
XMF.pop( "splinepoints" );
#Animations
len_anims = len( carma.possibleanimations );
if( len_anims > 0 ):
XMF.push( "animations" );
XMF.writeline( "count", len_anims );
for s in carma.possibleanimations:
XMF.writearraydata( s, 1 );
XMF.push( "animations" );
XMF.pop( "armature" );
XMF.commit();
XMF.pop( "armatures" );
progress_curr = 20;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out channels
XMF.setprecision( 4 );
cchan_len = len( BDATA_channels );
if( cchan_len > 0 ):
XMF.push( "channels" );
XMF.writeline( "count", cchan_len );
for cchan in BDATA_channels:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
XMF.push( "channel" );
XMF.writeline( "name", cchan.name );
XMF.writeline( "target", cchan.target );
XMF.writeline( "timestart", cchan.time_start );
XMF.writeline( "timelength", cchan.time_end - cchan.time_start );
len_kpos = len( cchan.key_positions );
if( len_kpos > 0 ):
XMF.push( "keypositions" );
XMF.writeline( "count", len_kpos );
XMF.push( "keytimes" );
for kpos in cchan.key_positions:
XMF.writearraydata( [kpos.time - cchan.time_start], 1 );
XMF.pop( "keytimes" );
XMF.push( "positions" );
for kpos in cchan.key_positions:
XMF.writearraydata( [kpos.x, kpos.y, kpos.z], 3 );
XMF.pop( "positions" );
XMF.pop( "keypositions" );
len_kquat = len( cchan.key_quats );
if( len_kquat > 0 ):
XMF.push( "keyquaternions" );
XMF.writeline( "count", len_kquat );
XMF.push( "keytimes" );
for kquat in cchan.key_quats:
XMF.writearraydata( [kquat.time - cchan.time_start], 1 );
XMF.pop( "keytimes" );
XMF.push( "quaternions" );
for kquat in cchan.key_quats:
XMF.writearraydata( [kquat.x, kquat.y, kquat.z, kquat.w], 4 );
XMF.pop( "quaternions" );
#XMF.writeline( "count", len_kquat );
#XMF.push( "data" );
#for kquat in cchan.key_quats:
#XMF.push( "keyquaternion" );
#XMF.writeline( "time", kquat.time - cchan.time_start );
#XMF.writeline( "data", [kquat.x, kquat.y, kquat.z, kquat.w] );
#XMF.pop( "keyquaternion" );
#XMF.writearraydata( [kquat.time - cchan.time_start, kquat.x, kquat.y, kquat.z, kquat.w], 5 );
#XMF.pop( "data" );
XMF.pop( "keyquaternions" );
len_kmatrix = len( cchan.key_matrices );
if( len_kmatrix > 0 ):
XMF.push( "keymatrices" );
XMF.writeline( "count", len_kmatrix );
XMF.push( "keytimes" );
for kmat in cchan.key_matrices:
XMF.writearraydata( [kmat.time - cchan.time_start], 1 );
XMF.pop( "keytimes" );
XMF.push( "matrices" );
for kmat in cchan.key_matrices:
XMF.writearraydata( kmat.matrix, 9 );
XMF.pop( "matrices" );
#XMF.writeline( "count", len_kmatrix );
#XMF.push( "data" );
#for kmat in cchan.key_quats:
#d = [kmat.time - cchan.time_start];
#d.extend( kmat.matrix );
#XMF.push( "keymatrix" );
#XMF.writeline( "time", kmat.time - cchan.time_start );
#XMF.writeline( "data", kmat.matrix );
#XMF.pop( "keymatrix" );
#XMF.writearraydata( d, 10 );
#XMF.pop( "data" );
XMF.pop( "keymatrices" );
len_kscale = len( cchan.key_scales );
if( len_kscale > 0 ):
XMF.push( "keyscales" );
XMF.writeline( "count", len_kscale );
XMF.push( "keytimes" );
for kscale in cchan.key_scales:
XMF.writearraydata( [kscale.time - cchan.time_start], 1 );
XMF.pop( "keytimes" );
XMF.push( "scales" );
for kscale in cchan.key_scales:
XMF.writearraydata( [kscale.x, kscale.y, kscale.z], 3 );
XMF.pop( "scales" );
#XMF.writeline( "count", len_kscale );
#XMF.push( "data" );
#for kscale in cchan.key_scales:
#XMF.push( "keyscale" );
#XMF.writeline( "time", kscale.time - cchan.time_start );
#XMF.writeline( "data", [kscale.x, kscale.y, kscale.z] );
#XMF.pop( "keyscale" );
#XMF.writearraydata( [kscale.time - cchan.time_start, kscale.x, kscale.y, kscale.z], 4 );
#XMF.pop( "data" );
XMF.pop( "keyscales" );
XMF.pop( "channel" );
XMF.commit();
XMF.pop( "channels" );
progress_curr = 30;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out animations
XMF.setprecision( 4 );
canim_len = len( BDATA_animations );
if( canim_len > 0 ):
XMF.push( "animations" );
XMF.writeline( "count", canim_len );
for canim in BDATA_animations:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
XMF.push( "animation" );
XMF.writeline( "name", canim.name );
XMF.writeline( "timestart", canim.time_start );
XMF.writeline( "timelength", canim.time_end - canim.time_start );
XMF.writeline( "timescale", canim.time_scale );
blendchanlen = len( canim.blendchannels );
XMF.writeline( "count", blendchanlen );
#XMF.push( "data" );
for blch in canim.blendchannels:
XMF.push( "blendchannel" );
XMF.writeline( "name", blch.name );
XMF.writeline( "timestart", blch.time_start );
XMF.writeline( "timelength", blch.time_end - blch.time_start );
XMF.writeline( "timescale", blch.time_scale );
#Blends... Hm. Don;t like this.
bbk_len = len( blch.blends );
if( bbk_len > 0 ): #If we actually blend the animation, write out the data.
XMF.writeline( "count", bbk_len );
XMF.push( "times" );
for bbkd in blch.blends:
XMF.writearraydata( [bbkd.time], 1 );
XMF.pop( "times" );
XMF.push( "weights" );
for bbkd in blch.blends:
XMF.writearraydata( [bbkd.blend], 1 );
XMF.pop( "weights" );
XMF.pop( "blendchannel" );
#XMF.pop( "data" );
XMF.pop( "animation" );
XMF.commit();
XMF.pop( "animations" );
progress_curr = 40;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out meshes
XMF.setprecision( 3 );
cmesh_len = len( BDATA_meshes );
if( cmesh_len > 0 ):
XMF.push( "meshes" );
XMF.writeline( "count", cmesh_len );
for mesh in BDATA_meshes:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
#len_va_verts = len(mesh.va_verts);
#Texcoords = t
#Normals = n
#Tangents = T
#Colors = c
#Weights = w
#Coords = v
XMF.push( "mesh" );
XMF.writeline( "name", mesh.name );
totalformat = "";
#Texcoords = t
va_texcoords_len = len(mesh.va_texcoords);
if( va_texcoords_len > 0 ):
totalformat += "t";
XMF.push( "texcoords" );
XMF.writeline( "count", va_texcoords_len );
XMF.push( "data" );
for v in mesh.va_texcoords:
XMF.writedataline( [v.u, v.v] );
XMF.pop( "data" );
XMF.pop( "texcoords" );
#Normals = n
va_normals_len = len(mesh.va_normals);
if( va_normals_len > 0 ):
totalformat += "n";
XMF.push( "normals" );
XMF.writeline( "count", va_normals_len );
XMF.push( "data" );
for v in mesh.va_normals:
XMF.writedataline( [v.nx, v.ny, v.nz] );
XMF.pop( "data" );
XMF.pop( "normals" );
#Tangents = T
va_tangents_len = len(mesh.va_tangents);
if( va_tangents_len > 0 ):
totalformat += "T";
XMF.push( "tangents" );
XMF.writeline( "count", va_tangents_len );
XMF.push( "data" );
for v in mesh.va_tangents:
XMF.writedataline( [v.tx, v.ty, v.tz] );
XMF.pop( "data" );
XMF.pop( "tangents" );
XMF.commit();
#Colors = c
va_colors_len = len(mesh.va_colors);
if( va_colors_len > 0 ):
totalformat += "c";
XMF.push( "colors" );
XMF.writeline( "count", va_colors_len );
XMF.push( "data" );
for v in mesh.va_colors:
XMF.writedataline( [v.r, v.g, v.b, v.a] );
XMF.pop( "data" );
XMF.pop( "colors" );
#Weights = w
va_weights_len = len(mesh.va_weights);
if( va_weights_len > 0 ):
totalformat += "w";
XMF.push( "weightsets" );
XMF.writeline( "count", va_weights_len );
for v in mesh.va_weights:
vwlen = len( v.weights );
vilen = len( v.weight_indicies );
vnlen = len( v.weight_names );
XMF.push( "weightset" );
if( vwlen == vilen and vwlen == vnlen ):
len_gverts = len(v.weights);
XMF.writeline( "count", len_gverts );
XMF.writeline( "indicies", v.weight_indicies );
XMF.writeline( "weights", v.weights );
XMF.push( "bones" );
#XMF.writeline( "count", len(v.weight_names) ); #not needed since len of all arrays is equal
for V in v.weight_names:
XMF.writeline( "name", V );
XMF.pop( "bones" );
#XMF.writeline( "count", len_gverts );
#XMF.push( "data" );
#weight index name
#widex = 0;
#while( widex < vwlen ):
#XMF.push( "weight" );
#XMF.writeline( "bone", v.weight_names[widex] );
#XMF.writeline( "index", v.weight_indicies[widex] );
#XMF.writeline( "value", v.weights[widex] );
#XMF.pop( "weight" );
#widex += 1;
#XMF.pop( "data" );
else:
print "ERROR non-matching weight vectors!";
XMF.pop( "weightset" );
XMF.pop( "weightsets" );
#Coords = v
va_coords_len = len(mesh.va_coords);
if( va_coords_len > 0 ):
totalformat += "v";
XMF.push( "coords" );
XMF.writeline( "count", va_coords_len );
XMF.push( "data" );
for v in mesh.va_coords:
XMF.writedataline( [v.x, v.y, v.z] );
XMF.pop( "data" );
XMF.pop( "coords" );
XMF.commit();
#Vertex Array
if( len(mesh.va_verts) > 0 ):
XMF.push( "vertexarray" );
XMF.writeline( "format", totalformat );
XMF.writeline( "count", len(mesh.va_verts) * len(totalformat) );
XMF.push( "data" );
for V in mesh.va_verts:
data = [];
if( va_texcoords_len > 0 ):
if( V.uv >= 0 ):
data.append( V.uv );
else:
data.append( 0 );
if( va_normals_len > 0 ):
if( V.no >= 0 ):
data.append( V.no );
else:
data.append( 0 );
if( va_tangents_len > 0 ):
if( V.tangent >= 0 ):
data.append( V.tangent );
else:
data.append( 0 );
if( va_colors_len > 0 ):
if( V.color >= 0 ):
data.append( V.color );
else:
data.append( 0 );
if( va_weights_len > 0 ):
if( V.weight >= 0 ):
data.append( V.weight );
else:
data.append( 0 );
if( va_coords_len > 0 ):
if( V.co >= 0 ):
data.append( V.co );
else:
data.append( 0 );
XMF.writedataline( data );
XMF.pop( "data" );
XMF.pop( "vertexarray" );
#Vertex groups
len_va_groups = len( mesh.va_groups );
if( len_va_groups > 0 ):
XMF.push( "vertgroups" );
XMF.writeline( "count", len_va_groups );
for ggroup in mesh.va_groups:
XMF.push( "vertgroup" );
XMF.writeline( "name", ggroup.name );
#Calculate bounds (?) (hard?)
#<bounds>float float float float float float</bounds><!-- need a bounds (xmin ymin zmin xmax ymax zmax) for this group; so you can use bones as bounds, or groups as bounds -->
XMF.writeline( "count", len(ggroup.verts) );
len_gverts = len(ggroup.verts);
XMF.push( "indicies" );
for V in ggroup.verts:
XMF.writedataline( [V.index] );
XMF.pop( "indicies" );
XMF.push( "weights" );
for V in ggroup.verts:
XMF.writedataline( [V.weight] );
XMF.pop( "weights" );
#XMF.writeline( "count", len(ggroup.verts) );
#XMF.push("data");
#for V in ggroup.verts:
#XMF.writedataline( [V.index, V.weight] );
#XMF.pop("data");
XMF.pop( "vertgroup" );
XMF.pop( "vertgroups" );
XMF.commit();
#Polygroups
len_polygroups = len( mesh.ia_groups );
if( len_polygroups > 0 ):
XMF.push( "polygroups" );
XMF.writeline( "count", len_polygroups );
for NG in mesh.ia_groups:
XMF.push( "polygroup" );
XMF.writeline( "name", "polygon" + str( NG.index ) );
if( NG.polytype == 3 ):
XMF.writeline( "format", "triangles" );
elif( NG.polytype == 4 ):
XMF.writeline( "format", "quads" );
elif( NG.polytype == 2 ):
XMF.writeline( "format", "lines" );
elif( NG.polytype == 1 ):
XMF.writeline( "format", "points" );
else:
print "ERROR bad polytype!";
XMF.writeline( "vabegin", NG.start_va_index );
XMF.writeline( "vaend", NG.end_va_index );
if( len( NG.matrixpalette ) > 0 ):
XMF.push( "matrixlinks" );
XMF.writeline( "count", len(NG.matrixpalette) );
#tempwdata = [];
for wv in NG.matrixpalette :
XMF.push( "matrixlink" );
XMF.writeline( "armature", wv.arma_name );
XMF.writeline( "name", wv.bone_name );
XMF.writeline( "index", wv.index );
XMF.writeline( "bone", wv.bone_index );
XMF.pop( "matrixlink" );
XMF.pop( "matrixlinks" );
#
#We have lots of other polygroup data! Argh! Just toss it into a string map for now
#
XMF.push( "stringmap" );
#NG.mat -> mesh.material_names ???
try:
if mesh.material_names[ NG.mat ] != None:
matlinker = [ "mat", mesh.material_names[ NG.mat ] ];
else:
matlinker = [ "mat", "" ];
except:
print "#ERROR unmapped material: ",NG.mat;
matlinker = [ "mat", "" ];
tardata = [ matlinker, ["mode", NG.mode], ["image_name", NG.image_name], ["image_filename", NG.image_filename], ["transp", NG.transp], ["polytype", NG.polytype] ];
XMF.writeline( "count", len(tardata) );
for tarelm in tardata:
XMF.push( "link" );
XMF.writeline( "name", tarelm[0] );
XMF.writeline( "data", tarelm[1] );
XMF.pop( "link" );
XMF.pop( "stringmap" );
k_max = len( NG.indicies );
k = 0;
k_count = 0;
while( k < k_max ):
k_count += len( mesh.ia_faces[ NG.indicies[ k ] ].indicies );
k += 1;
XMF.writeline( "count", k_count );
XMF.push( "data" );
k = 0;
while( k < k_max ):
XMF.writearraydata( mesh.ia_faces[ NG.indicies[ k ] ].indicies, NG.polytype );
k += 1;
XMF.pop( "data" );
XMF.pop( "polygroup" );
XMF.pop( "polygroups" );
XMF.pop( "mesh" );
XMF.commit();
XMF.pop( "meshes" );
progress_curr = 50;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out textures
XMF.setprecision( 4 );
ctexture_len = len( BDATA_textures );
if( ctexture_len > 0 ):
XMF.push( "textures" );
XMF.writeline( "count", ctexture_len );
for T in BDATA_textures:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
XMF.push( "texture" );
XMF.writeline( "name", T.name );
XMF.writeline( "width", T.dimensions[0] );
XMF.writeline( "height", T.dimensions[1] );
#Hm.
XMF.writeline( "type", T.type );
XMF.writeline( "bpp", T.bpp );
finalstr = eASCIICompress( T.data, 1 ); #Can compress or not. #ERROR, missing option for exporter
XMF.push( "hexdata" );
XMF.writeline( "size", len( T.data ) );
XMF.writeline( "table", e6BitsToASCII ); #Define ASCII -> bit conversion (64 bit or more; table is flexible)
#XMF.writeline( "compression", "lzss" ); #Not until it actually works.
XMF.writeline( "count", len( finalstr ) );
XMF.writeline( "data", finalstr );
XMF.pop( "hexdata" );
XMF.pop( "texture" );
XMF.commit();
XMF.pop( "textures" );
progress_curr = 60;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out materials
XMF.setprecision( 4 );
cmatl_len = len( BDATA_materials );
if( cmatl_len > 0 ):
XMF.push( "materials" );
XMF.writeline( "count", cmatl_len );
for matdata in BDATA_materials:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
XMF.push( "material" );
XMF.writeline( "name", matdata.name );
XMF.writeline( "ambient", matdata.color_ambient );
XMF.writeline( "diffuse", matdata.color_diffuse );
XMF.writeline( "specular", matdata.color_specular );
XMF.writeline( "emissive", matdata.color_emissive );
XMF.writeline( "transparency", matdata.transparency );
len_textures = len( matdata.textures );
if( len_textures > 0 ):
XMF.push( "textures" );
XMF.writeline( "count", len_textures );
for texname in matdata.textures:
XMF.push( "texture" );
XMF.writeline( "name", texname );
XMF.writeline( "blend", 0 );
XMF.pop( "texture" );
XMF.pop( "textures" );
#self.specularity = 32; #specular highlight 'size'
#self.shaders = []; #List of shaders (by name)
XMF.pop( "material" );
XMF.pop( "materials" );
progress_curr = 70;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out models
XMF.setprecision( 4 );
cmodl_len = len( BDATA_models );
if( cmodl_len > 0 ):
XMF.push( "models" );
XMF.writeline( "count", cmodl_len );
for modeldata in BDATA_models:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
XMF.push( "model" );
XMF.writeline( "name", matdata.name );
XMF.pop( "model" );
XMF.pop( "models" );
progress_curr = 80;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
XMF.commit();
#Write out scenes
XMF.setprecision( 4 );
cscene_len = len( BDATA_scenes );
if( cscene_len > 0 ):
XMF.push( "scenes" );
XMF.writeline( "count", cscene_len );
cscene_count = 0;
for cscene in BDATA_scenes:
if( eInstantQuit() ): GLOBAL_QUIT = 1; return;
XMF.push( "scene" );
if( len( cscene.name ) < 1 ):
XMF.writeline( "name", "scene" + str(cscene_count) );
else:
XMF.writeline( "name", cscene.name );
len_objects = len( cscene.objects );
if( len_objects > 0 ):
XMF.push( "objects" );
XMF.writeline( "count", len_objects );
for obj in cscene.objects:
XMF.push( "object" );
XMF.writeline( "name", obj.name );
if( obj.parent != None ):
XMF.writeline( "parent", obj.parent );
#parent bone? parent vertex? Other annoying modifications? Hm. #ERROR
XMF.writeline( "depth", obj.depth );
XMF.writeline( "position", obj.pos );
#No quaternion? Hm. good!
XMF.push("matrix");
XMF.writearraydata( obj.matrix, 3 );
XMF.pop("matrix");
XMF.writeline( "scale", obj.scale );
#??? object stuff?
#Properties (from game engine)
len_props = len( obj.properties );
if( len_props > 0 ):
XMF.push( "values" );
XMF.writeline( "count", len_props );
for proppy in obj.properties:
XMF.push( "value" );
XMF.writeline( "name", proppy.name );
XMF.writeline( "type", proppy.type );
XMF.writeline( "data", proppy.data );
XMF.pop( "value" );
XMF.pop( "values" );
len_links = len( obj.links );
if( len_links > 0 ):
XMF.push( "links" );
XMF.writeline( "count", len_links );
for linky in obj.links:
XMF.push( "link" );
XMF.writeline( "name", linky.name );
XMF.writeline( "type", linky.type );
XMF.pop( "link" );
XMF.pop( "links" );
XMF.pop( "object" );
XMF.commit();
XMF.pop( "objects" );
XMF.pop( "scene" );
cscene_count += 1;
XMF.pop( "scenes" );
XMF.pop( "ted" );
progress_curr = 90;
eDisplayProgressBarOption( progress_start + ( float(progress_curr) / float(progress_count) )*(progress_end - progress_start), "Write...", GLOBAL_DEBUG_SHOW_PROGRESS );
#
#----------------------------------------------------------------------
#
#Now that we have all the data stored in memory, write it out as text from that array
#
XMF.commit();
SR = eGenerateStringHash( XMF.finaldata );
XMF.writereplace( "checksum", SR, xml_file_uid_depth, xml_file_uid_start, xml_file_uid_end );
#
#Could also build a reference table?
#
eDisplayProgressBar( 0.98, "Write File..." );
#Always export the DTD ?
#txt.write( "<!--\n" );
#txt.write( const_DTD_ted );
#txt.write( "-->\n" );
txt.write( XMF.finaldata );
#
#Convert the DTD into a ENUM & cstring for C code; placve in log file
#
if( True ):
log.write( "/*\n" );
log.write( "<!--\n" );
log.write( const_DTD_ted );
log.write( "-->\n" );
log.write( "*/\n\n" );
Cstring = eStringToCString( const_DTD_ted );
log.write( "static const unsigned int dtd_ted_size = " + str( len( const_DTD_ted ) ) + ";\n" );
log.write( "static const char * dtd_ted = \"" );
log.write( eStringToCString( const_DTD_ted ) );
log.write( "\";\n\n" );
log.write( "enum enum_ted{\n" );
Etree = convert_DTD_to_ENUM( const_DTD_ted );
for X in Etree:
log.write( X[0] + " = "+str(X[1])+",\n" );
log.write( "};\n\n" );
#Write some C-Code for converting ASCII compressed data to <-> from; makes everyone a little less nervous.
#Could also convert to straight push/pop/pushdata code... which is creepy, but also effective.
#
#----------------------------------------------------------------------
#
#Memory cleanup
#
del BDATA_armatures;
del BDATA_channels;
del BDATA_animations;
del BDATA_meshes;
del BDATA_textures;
del BDATA_materials;
del BDATA_models;
del BDATA_scenes;
#----------------------------------------------------------------------
#
#Done!
#
eDisplayProgressBar( 1.0, "Done!" );
debug_time_final = eGetTime();
debug_time_count = (debug_time_final - debug_time_init);
#eLog( "Finished! Time: %0.1f seconds" % (debug_time_count), log );
eDisplayPopUp( "Done: %0.1f seconds" % (debug_time_count) );
return;
#####################################################################
#
#Yay main!
#
def main( filename ):
#
#globals (flags for export)
#
global GLOBAL_LOADED_ONCE;
global GLOBAL_FILE_NAME;
global GLOBAL_FILE_LOG_NAME;
global GLOBAL_USE_OBJECTS;
global GLOBAL_USE_MESHES;
global GLOBAL_USE_ARMATURES;
global GLOBAL_USE_MATERIALS;
global GLOBAL_MAX_SPLIT_BONES;
global GLOBAL_TRANSFORM_FORWARD;
global GLOBAL_TRANSFORM_UP;
global GLOBAL_TRANSFORM_BONE_FORWARD;
global GLOBAL_TRANSFORM_BONE_UP;
global GLOBAL_DATAGROUP_NAME;
global GLOBAL_CONVERT_QUADS;
global GLOBAL_CONST_REGKEY;
global GLOBAL_TRANSMATRIX;
global GLOBAL_TRANSMATRIX_BONE;
global GLOBAL_DEBUG_SHOW_PROGRESS;
global GLOBAL_QUIT;
#
if filename == "":
#
#Quit if we tried loading again
#
if( GLOBAL_LOADED_ONCE != 0 ):
return 0;
#
#PuP a menu for options first
#
GLOBAL_LOADED_ONCE += 1;
rdict = Blender.Registry.GetKey( GLOBAL_CONST_REGKEY, True);
#if rdict:
try:
filename = rdict['last_filename'];
except:
filename = "";
try:
GLOBAL_USE_OBJECTS = rdict['global_use_objects'];
except:
GLOBAL_USE_OBJECTS = True;
try:
GLOBAL_USE_MESHES = rdict['global_use_meshes'];
except:
GLOBAL_USE_MESHES = True;
try:
GLOBAL_USE_ARMATURES = rdict['global_use_armatures'];
except:
GLOBAL_USE_ARMATURES = True;
try:
GLOBAL_USE_MATERIALS = rdict['global_use_materials'];
except:
GLOBAL_USE_MATERIALS = True;
try:
GLOBAL_MAX_SPLIT_BONES = rdict['global_max_split_bones'];
except:
GLOBAL_MAX_SPLIT_BONES = 28;
try:
GLOBAL_TRANSFORM_FORWARD = rdict['global_transform_forward'];
except:
GLOBAL_TRANSFORM_FORWARD = "+X";
try:
GLOBAL_TRANSFORM_UP = rdict['global_transform_up'];
except:
GLOBAL_TRANSFORM_UP = "+Z";
try:
GLOBAL_TRANSFORM_BONE_FORWARD = rdict['global_transform_bone_forward'];
except:
GLOBAL_TRANSFORM_BONE_FORWARD = "+Y";
try:
GLOBAL_TRANSFORM_BONE_UP = rdict['global_transform_bone_up'];
except:
GLOBAL_TRANSFORM_BONE_UP = "+Z";
try:
GLOBAL_FILE_LOG_NAME = rdict['global_logname'];
except:
GLOBAL_FILE_LOG_NAME = "tedlog.txt";
try:
GLOBAL_DATAGROUP_NAME;
except:
GLOBAL_DATAGROUP_NAME = "Blender";
try:
GLOBAL_CONVERT_QUADS;
except:
GLOBAL_CONVERT_QUADS = True;
#else:
GLOBAL_DATAGROUP_NAME = Blender.Get('filename');
#
#Create each gui component
#
GUI_LOG_NAME = eDisplayCreate( GLOBAL_FILE_LOG_NAME );
GUI_MAX_BONES = eDisplayCreate( GLOBAL_MAX_SPLIT_BONES );
GUI_USE_MESHES = eDisplayCreate( GLOBAL_USE_MESHES );
GUI_USE_MATERIALS = eDisplayCreate( GLOBAL_USE_MATERIALS );
GUI_USE_ARMATURES = eDisplayCreate( GLOBAL_USE_ARMATURES );
GUI_USE_OBJECTS = eDisplayCreate( GLOBAL_USE_OBJECTS );
GUI_TRANSFORM_FORWARD = eDisplayCreate( GLOBAL_TRANSFORM_FORWARD );
GUI_TRANSFORM_UP = eDisplayCreate( GLOBAL_TRANSFORM_UP );
GUI_TRANSFORM_BONE_FORWARD = eDisplayCreate( GLOBAL_TRANSFORM_BONE_FORWARD );
GUI_TRANSFORM_BONE_UP = eDisplayCreate( GLOBAL_TRANSFORM_BONE_UP );
GUI_CONVERT_QUADS = eDisplayCreate( GLOBAL_CONVERT_QUADS );
GUI_CLEAR_SETTINGS = eDisplayCreate( False );
GUI_DATAGROUP_NAME = eDisplayCreate( GLOBAL_DATAGROUP_NAME );
#
#Format a block of popup data
#
pup_block = [\
('Use Objects', GUI_USE_OBJECTS, 'Objects are exported'),\
('Use Meshes', GUI_USE_MESHES, 'Meshes are exported'),\
('Use Armatures', GUI_USE_ARMATURES, 'Armatures are exported'),\
('Max Bones: ', GUI_MAX_BONES, 1, 511, 'Adjusts the strip splitting algorithms maximum bones per split'),\
('Use Materials', GUI_USE_MATERIALS, 'Materials are exported'),\
('Convert Quads', GUI_CONVERT_QUADS, 'Quads are converted to triangles'),\
('Log: ', GUI_LOG_NAME, 0, 88, 'Log filename' ),\
('Axis Forward: ', GUI_TRANSFORM_FORWARD, 0, 2, 'Axis that is converted to Forward, +X +Y +Z -X -Y -Z'),\
('Axis Up: ', GUI_TRANSFORM_UP, 0, 2, 'Axis that is converted to Up, +X +Y +Z -X -Y -Z'),\
('Bone Forward: ', GUI_TRANSFORM_BONE_FORWARD, 0, 2, 'Bone Axis that is converted to Forward, +X +Y +Z -X -Y -Z'),\
('Bone Up: ', GUI_TRANSFORM_BONE_UP, 0, 2, 'Bone Axis that is converted to Up, +X +Y +Z -X -Y -Z'),\
('DataGroup: ', GUI_DATAGROUP_NAME, 0, 88, 'Name of this data group for resource treeing' ),\
('*Clear Settings*', GUI_CLEAR_SETTINGS, 'Removes all registry data'),\
];
#
#display it
#
if( not eDisplayFullPopup( 'TED Exporter: '+__version__, pup_block ) ):
return 0;
#
#Save configured options
#
GLOBAL_FILE_NAME = filename;
GLOBAL_FILE_LOG_NAME = GUI_LOG_NAME.val;
GLOBAL_USE_OBJECTS = GUI_USE_OBJECTS.val;
GLOBAL_USE_MESHES = GUI_USE_MESHES.val;
GLOBAL_USE_ARMATURES = GUI_USE_ARMATURES.val;
GLOBAL_MAX_SPLIT_BONES = GUI_MAX_BONES.val;
GLOBAL_USE_MATERIALS = GUI_USE_MATERIALS.val;
GLOBAL_TRANSFORM_FORWARD = GUI_TRANSFORM_FORWARD.val;
GLOBAL_TRANSFORM_UP = GUI_TRANSFORM_UP.val;
GLOBAL_TRANSFORM_BONE_FORWARD = GUI_TRANSFORM_BONE_FORWARD.val;
GLOBAL_TRANSFORM_BONE_UP = GUI_TRANSFORM_BONE_UP.val;
GLOBAL_DATAGROUP_NAME = GUI_DATAGROUP_NAME.val;
GLOBAL_CONVERT_QUADS = GUI_CONVERT_QUADS.val;
GLOBAL_TRANSMATRIX = eConvertCoordinates( GLOBAL_TRANSFORM_FORWARD, GLOBAL_TRANSFORM_UP );
GLOBAL_TRANSMATRIX_BONE = eConvertCoordinates( GLOBAL_TRANSFORM_BONE_FORWARD, GLOBAL_TRANSFORM_BONE_UP );
#
# Error check values
#
#GLOBAL_DATAGROUP_NAME cannot have any symbols <, >, or & in it.
if( GLOBAL_TRANSMATRIX == None ):
print "ERROR - invalid axes provide for transform; use ONLY +X +Y +Z -X -Y -Z";
GLOBAL_TRANSFORM_FORWARD = "+X";
GLOBAL_TRANSFORM_UP = "+Z";
GLOBAL_TRANSMATRIX = eConvertCoordinates( GLOBAL_TRANSFORM_FORWARD, GLOBAL_TRANSFORM_UP );
if( GLOBAL_TRANSMATRIX_BONE == None ):
print "ERROR - invalid axes provide for bone transform; use ONLY +X +Y +Z -X -Y -Z";
GLOBAL_TRANSFORM_BONE_FORWARD = "+Y";
GLOBAL_TRANSFORM_BONE_UP = "+Z";
GLOBAL_TRANSMATRIX_BONE = eConvertCoordinates( GLOBAL_TRANSFORM_BONE_FORWARD, GLOBAL_TRANSFORM_BONE_UP );
#clear em!
if( GUI_CLEAR_SETTINGS.val ):
GLOBAL_USE_OBJECTS = True;
GLOBAL_USE_MESHES = True;
GLOBAL_USE_ARMATURES = True;
GLOBAL_USE_MATERIALS = True;
GLOBAL_MAX_SPLIT_BONES = 28;
GLOBAL_TRANSFORM_FORWARD = "+X";
GLOBAL_TRANSFORM_UP = "+Z";
GLOBAL_TRANSFORM_BONE_FORWARD = "+Y";
GLOBAL_TRANSFORM_BONE_UP = "+Z";
GLOBAL_FILE_NAME = "";
GLOBAL_FILE_LOG_NAME = "tedlog.txt";
GLOBAL_LOADED_ONCE = 0;
GLOBAL_DATAGROUP_NAME = "Blender";
GLOBAL_CONVERT_QUADS = True;
#
#Save filename & path to registry on next load? Hm.
#
buildregkeys = {
'last_filename':GLOBAL_FILE_NAME,
'global_use_objects':GLOBAL_USE_OBJECTS,
'global_use_meshes':GLOBAL_USE_MESHES,
'global_use_armatures':GLOBAL_USE_ARMATURES,
'global_use_materials':GLOBAL_USE_MATERIALS,
'global_max_split_bones':GLOBAL_MAX_SPLIT_BONES,
'global_transform_forward':GLOBAL_TRANSFORM_FORWARD,
'global_transform_up':GLOBAL_TRANSFORM_UP,
'global_transform_bone_forward':GLOBAL_TRANSFORM_BONE_FORWARD,
'global_transform_bone_up':GLOBAL_TRANSFORM_BONE_UP,
'global_logname':GLOBAL_FILE_LOG_NAME,
'global_datagroup_name':GLOBAL_DATAGROUP_NAME,
'global_convert_quads':GLOBAL_CONVERT_QUADS,
};
Blender.Registry.SetKey( GLOBAL_CONST_REGKEY, buildregkeys, True);
else:
eDisplayFileSelector( main, "Export", filename );
else:
GLOBAL_FILE_NAME = filename;
#
#Save filename & path to registry on next load? Hm.
#
buildregkeys = {
'last_filename':GLOBAL_FILE_NAME,
'global_use_objects':GLOBAL_USE_OBJECTS,
'global_use_meshes':GLOBAL_USE_MESHES,
'global_use_armatures':GLOBAL_USE_ARMATURES,
'global_use_materials':GLOBAL_USE_MATERIALS,
'global_max_split_bones':GLOBAL_MAX_SPLIT_BONES,
'global_transform_forward':GLOBAL_TRANSFORM_FORWARD,
'global_transform_up':GLOBAL_TRANSFORM_UP,
'global_transform_bone_forward':GLOBAL_TRANSFORM_BONE_FORWARD,
'global_transform_bone_up':GLOBAL_TRANSFORM_BONE_UP,
'global_logname':GLOBAL_FILE_LOG_NAME,
'global_datagroup_name':GLOBAL_DATAGROUP_NAME,
'global_convert_quads':GLOBAL_CONVERT_QUADS,
};
Blender.Registry.SetKey( GLOBAL_CONST_REGKEY, buildregkeys, True);
#
#NOW open the file and export it, depending on how the file was to be opened
#
txt = None;
#if FILE_INTERNAL:
# txt = Text.New( GLOBAL_FILE_NAME );
#else:
txt = eCreateNewExternalText( filename );
log = eCreateNewInternalText( GLOBAL_FILE_LOG_NAME );
if txt != None and log != None:
print "Running...";
TEDexport( txt, log );
if( GLOBAL_QUIT ):
print "*** Aborted";
eDisplayProgressBar( 0.1, "Aborted" );
else:
print "Done!";
eDisplayProgressBar( 1.0, "Done!" );
else:
print "*** Error, txt or log was NULL";
eDisplayProgressBar( 0.1, "ERROR!" );
txt.close();
#if not FILE_INTERNAL:
# txt.close();
return;
######################################################################
#
#main entry point
#
if __name__ == '__main__':
global GLOBAL_LOADED_ONCE;
global GLOBAL_CONST_REGKEY;
global GLOBAL_DEBUG_SHOW_PROGRESS;
global GLOBAL_QUIT;
GLOBAL_QUIT = 0;
GLOBAL_DEBUG_SHOW_PROGRESS = 1;
GLOBAL_CONST_REGKEY = 'igtl_export_ted_lastfile';
GLOBAL_LOADED_ONCE = 0;
main( "" );
igtl_export_ted.py
By ImaginaryZ -- Report
2010-06-04 .TED file exporter for Blender 2.49b
Rename file to igtl_export_ted.py;
Place in Blender/.blender/scripts; or in your python scripts directory.
In Blender, Go to File->Export->IGTL .TED to export current selection and scene.
Currently;
Not much left that isn't working; Specs are going to undergo a revision soon; automatic c-enum + DTD generation for no-confusion interoperable specifications works perfectly; many errors corrected + testing passed again. Animations work great; stripping and more materials needed; armature<->parenting fix needed
"What is this?"
This is a python script for blender 2.49.2; It allows you to export data out of blender, in other words, it converts data in blender to a human readable + editable xml file so you can load it / convert it to whatever you like; It also performs a lot of conversions so the exported data is hardware - ready;
The idea is, we (developers) need "interchange" formats, so that the same file type can be created from many programs. In this way, we can generate game media that can be loaded/saved to this "interchange" format, which has a rigid and very well documented specification. This allows game content to be highly flexible before it is compiled into runtime data (IE installation). For making independent games like I do, these types of formats define resource generation and management; allowing me to easily manage a large project by providing a universal tool + file format to editors and game players alike. This also makes custom content creation easy, and debugging and tweaking even easier.
More details will eventually be explained on my real blog.

Please login to post a comment.
Posted by Generationslayer 16 years ago Report
i don't understand O.o