#!BPY """ Name: 'IGTL TED (dxml)' Blender: 249.2 Group: 'Export' Tooltip: 'Export a TED resource file from selection (DESTRUCTIVE)' """ __author__ = "ImaginaryZ" __url__ = ("blender", "gocaco") __version__ = "2011-04-02" __bpydoc__ = """\ Exports all selected data to a TED Resource File (Text Extracted Data) which is a DXML document (ASCII text Duane XML format) Now can convert the final TED DXML to a JSON format for WebGL programs. JSON exporting removed because it's dumb. #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. (convert to modifyer) ***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; Added script actions; need to use IPO channels too for object animations Shapes - Preliminary work copies GE shapes; 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 - defined by linkages in mesh Scenes - Mostly complete; Exports all objects in all scenes; Exports properties, links and world positions/orientations (may not be correctly oriented) Objects - Mostly complete; missing some data from objects Zoning - Appended to scenes, allows specialized grouping operations in a scene 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, nope, correct, 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: Whooopee! Yes! is NOT the same as: Whooopee! Yes! 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 and tag, which obviously have the data needed. some tags specify a array of other tags; this is fine and does not need the 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 tags; Which are Hex->ASCII saved, as defined by a translation table. You will notice the exporter ALSO generates the C,C++-Code to define the exact format specification for the file, as well as C-Code for ENUM's for your loading routine. TED files work in any XML loader. #Texcoords = t #Normals = n #Tangents = T #Colors = c #Weights = w #Coords = v 2 ... ... 2 ... ... 2 0.0 -16.0 12.0 Name 0.75 1 Name 0.75 1 Name 1 0.75 """ #from igtl_include_ted import * #could make a utility set, and use it commonly among the importer/exporter 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 coordinates name string handedness string basis float 9 coordinate float 3 quaternion float 4 matrix string units string scale float 3 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 chainweights float splinepoints count uint 1 splinepoint direction float 3 position float 3 links count uint 1 link bone string weight float 1 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 keyscripts count uint 1 keytimes float scripts script string 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 shaders count uint 1 shader name string type string params count uint 1 param name string type string script string 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 vshader name string type string params count uint 1 param name string type string value string fshader name string type string params count uint 1 param name string type string value string geoshapes count uint 1 geoshape type string pointcenter float 3 pointmin float 3 pointmax float 3 count uint 1 params float 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 script string zoning zones count uint 1 zone name string min float 3 max float 3 objects count uint 1 object name string shared uint 1 links count uint 1 name string zplanes count uint 1 zplane name string position float 3 matrix float 9 quaternion float 4 size float 3 from string to string """; #Script "new line" keyword const_animation_script_keyword = "+EVENT"; #Your code may NOT have this sequence: "= len( line_stack ) ): line_stack.append( [] ); text_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" text_stack[ depth ] = name; lex_stack = text_stack[:depth + 1]; #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, lex_stack ] ); 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 and for this tag, where 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 and for this tag, where 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 += "\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 += "\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 ] + "\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 ] + "\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 = "\n"; dtdstring += "\t" + str(len(sortkeys)) + "\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 ] + "" + str(dtdelem.depth) + ":" + str(dtdelem.element_count) + "/" + str(dtdelem.is_fixed_array) + ":" + strtype + ":" + taglist + " " + dtdelem.tagname + ""; linestr += "\n" dtdstring += linestr; dtdstring += "\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; class cKScript: def __init__(self): self.index = -1; self.time = 0.0; self.script = ""; 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 = []; self.key_scripts = []; # #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 geometric shape # class cGeoShape: def __init__(self): self.index = -1; self.name = ""; self.type = ""; self.pointcenter = [0,0,0]; self.pointmin = [0,0,0]; self.pointmax = [0,0,0]; self.params = []; def get_key(self): #Return the hash key for this uv coordinate S = str(self.name) + " "+str(self.type) + " "+str(self.pointcenter) + " "+str(self.pointmin) + " "+str(self.pointmax) + " "+str(self.params); return S; # #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.minmax = None;#[0,0,0,0,0,0]; 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 = []; self.btype = ""; # #Stores a complete scene, includes linking information # class cScene: class cSZone: def __init__(self): self.index = -1; self.name = ""; self.minpos = []; self.maxpos = []; self.objs = []; self.links = []; class cSZplane: def __init__(self): self.index = -1; self.name = ""; self.pos = []; self.matrix = []; self.quat = None; self.size = []; self.links = []; #Must be exactly length 2, and both links must exist def __init__(self): self.index = -1; self.name = ""; self.objects = []; self.zones = []; self.zplanes = []; ###################################################################### # #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; #Returns the character type (0 = whitespace, 1 = numeric, 2 = float numeric, 3 = string def eCharType( c ): if( c == '\n' or c == ' ' or c == '\r' or c == '\t' ): return 0; else: if( c >= '0' and c <= '9' ): return 1; elif( c == '+' or c == '-' ): return 1; elif( c == '.' ): return 2; return 3; #-------------------------------------------------------------------- # #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 ); #-------------------------------------------------------------------- # #Get all blender texts # def eGetAllTexts(): return Blender.Text.Get(); #-------------------------------------------------------------------- # #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; global GLOBAL_USE_JSON; global GLOBAL_USE_TEXTURES; 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 = []; blender_texts = eGetAllTexts(); #get all text objects in the scene. 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 "#WARNING No Pose; thus no IK"; #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: chanindex = all_channels[ chankey ]; chand = BDATA_channels[ chanindex ]; #Add this channel to this animation? if( timefirst ): min_time = chand.time_start; max_time = chand.time_end; timefirst = 0; else: if( chand.time_start < min_time ): min_time = chand.time_start; if( chand.time_start > max_time ): max_time = chand.time_start; if( chand.time_end < min_time ): min_time = chand.time_end; if( chand.time_end > max_time ): max_time = chand.time_end; expanim.time_start = min_time; expanim.time_end = max_time; #For each action, there can be a text document with "action code" # #In blender, this is written from "AnimationName.e"; the script inside varies per program. # #We also defined some constants that manipulate this as needed # #ERROR #ERROR lenasckwrd = len( const_animation_script_keyword ); for T in blender_texts: Tname = T.getName(); laname = len( expanim.name ); if( (len( Tname ) - len( expanim.name )) >= 0 ): if( Tname[0:laname] == expanim.name[0:laname] ): # #ERROR missing nomenclature for scripts #Use this text file as anim data? tlines = T.asLines(); prevframetime = -1000000; allframeevents = []; appcode = ""; linecount = 0; for line in tlines: lenline = len(line); if( lenline > 0 ): #ignore empty lines if( line[0] != '#' ): #ignore comments if( lenline > lenasckwrd ): if( line[0:lenasckwrd] == const_animation_script_keyword[0:lenasckwrd] ): if( line[lenasckwrd] == ' ' ): #Determine frame time (hm) fstring = line[lenasckwrd:]; tframe = float( fstring ); if( tframe >= expanim.time_start and tframe <= expanim.time_end ): #frame time is good and valid! Let's use it. if( len( appcode ) > 0 ): #append code to previous event allframeevents.append( [ prevframetime, appcode] ); appcode = ""; prevframetime = tframe; else: #Malformed +EVENT line, frame time is wrong! print "#ERROR - Line "+str(linecount)+" File"+ Tname +" frame time is wrong!"; else: #Malformed +EVENT line! missing space & time print "#ERROR - Line "+str(linecount)+" File"+ Tname +" missing space!"; else: appcode += line; #append code line. appcode += "\n"; else: appcode += line; #append code line. appcode += "\n"; linecount += 1; if( len( appcode ) > 0 ): allframeevents.append( [ prevframetime, appcode] ); if( len( allframeevents ) > 0 ): #Sort by time allframeevents.sort( eSortByFirstElement ); #Now convert all parsed frame events into a unique TED event channel! (AnimName.code?) expchannel = cChannel(); expchannel.name = action_key + "@" + "script"; #Let's hope this is unique... expchannel.target = ""; #No target bone? expchannel.time_start = allframeevents[0][0]; expchannel.time_end = allframeevents[-1][0]; if( timefirst ): min_time = expchannel.time_start; max_time = expchannel.time_end; timefirst = 0; else: if( expchannel.time_start < min_time ): min_time = expchannel.time_start; if( expchannel.time_start > max_time ): max_time = expchannel.time_start; if( expchannel.time_end < min_time ): min_time = expchannel.time_end; if( expchannel.time_end > max_time ): max_time = expchannel.time_end; expchannel.key_scripts = []; for evubbu in allframeevents: ftime = evubbu[0]; fdata = evubbu[1]; ndata = cChannel.cKScript(); ndata.time = ftime; ndata.index = len( expchannel.key_scripts ); ndata.script = fdata; expchannel.key_scripts.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; print "#INFO Script channel used: "+expchannel.name; BDATA_channels_curr += 1; expanim.time_start = min_time; expanim.time_end = max_time; 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; nbch.blends = []; bl = cAnimation.cKBlend(); bl.time = nbch.time_start; bl.blend = 1.0; nbch.blends.append( bl ); expanim.blendchannels.append( nbch ); 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 = []; try: 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? except: print "#Warning; Group has no deforms verts: "; pass; # #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: #ERROR #ERROR #ERROR why does this happen? #print 'ERROR Bone not in matrix palette! Impossible!'; newWEIGHT.weight_indicies[wi] = -1; #ERROR! curde = len( newWEIGHT.weight_names ) - 1; #del newWEIGHT.weights[ curde ]; #del newWEIGHT.weight_indicies[ curde ]; #del newWEIGHT.weight_names[ curde ]; 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 "#WARNING 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; if( not GLOBAL_USE_TEXTURES ): break; 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 # ERROR! 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_ignore_me = 0; nobj = cObject(); nobj.btype = ""; 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 ); # #Linkage # nobj.links = []; # #Physics & Shape properties! # #phys = gPhysics(); #phys.name = blobj.name; # global_minmax = None; #Array of 6 values, min x,y,z, max x,y,z local_minmax = None; #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) #Repair the aabb's so that they are MIN / MAX pairs? global_minmax = [ global_aabb[0][0], global_aabb[0][1], global_aabb[0][2], global_aabb[0][0], global_aabb[0][1], global_aabb[0][2] ]; local_minmax = [ local_aabb[0][0], local_aabb[0][1], local_aabb[0][2], local_aabb[0][0], local_aabb[0][1], local_aabb[0][2] ]; for v in global_aabb: # if( v[0] < global_minmax[0] ): global_minmax[0] = v[0]; elif( v[0] > global_minmax[3] ): global_minmax[3] = v[0]; if( v[1] < global_minmax[1] ): global_minmax[1] = v[1]; elif( v[1] > global_minmax[4] ): global_minmax[4] = v[1]; if( v[2] < global_minmax[2] ): global_minmax[2] = v[2]; elif( v[2] > global_minmax[5] ): global_minmax[5] = v[2]; # if( v[0] < local_minmax[0] ): local_minmax[0] = v[0]; elif( v[0] > local_minmax[3] ): local_minmax[3] = v[0]; if( v[1] < local_minmax[1] ): local_minmax[1] = v[1]; elif( v[1] > local_minmax[4] ): local_minmax[4] = v[1]; if( v[2] < local_minmax[2] ): local_minmax[2] = v[2]; elif( v[2] > local_minmax[5] ): local_minmax[5] = v[2]; except: local_aabb = None; global_aabb = None; global_minmax = None; local_minmax = None; nobj.minmax = global_minmax; #GLOBAL minmax ? #Use global/local minmax instead of AABB; they're much more logical. #global_minmax = minx, miny, minz, maxx, maxy, maxz 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; bRadius = obj.rbRadius; bMass = obj.rbMass; # if( bFlags != None ): # if( bFlags & Blender.Object.RBFlags['ACTOR'] ): #"Enable evaluation by game engine" pass; if( bFlags & Blender.Object.RBFlags['DYNAMIC'] ): #Requires ACTOR "Enable physical motion" pass; if( not (bFlags & Blender.Object.RBFlags['GHOST']) ): #Requires ACTOR "Enables objets that don't restitude collisions" pass; if( bFlags & Blender.Object.RBFlags['RIGIDBODY'] ): #Requires ACTOR, DYNAMIC "Enable rolling physics; rotations" pass; if( bFlags & Blender.Object.RBFlags['COLLISION_RESPONSE'] ): #Requires ACTOR, DYNAMIC "Disable auto-deactivation" pass; if( bFlags & Blender.Object.RBFlags['USEFH'] ): #Requires ACTOR, DYNAMIC "Use Fh settings in Materials" pass; if( bFlags & Blender.Object.RBFlags['ROTFH'] ): #Requires ACTOR, DYNAMIC "Use face normal to rotate object" pass; if( bFlags & Blender.Object.RBFlags['ANISOTROPIC'] ): #Requires ACTOR, DYNAMIC "Enable ansio physics" pass; # if( bFlags & Blender.Object.RBFlags['SECTOR'] ): #Unused "All game elements should be in the sector bounds box" pass; if( bFlags & Blender.Object.RBFlags['PROP'] ): #Unused; "Object fixed within a sensor" pass; if( bFlags & Blender.Object.RBFlags['BOUNDS'] ): #Unused "Specify a bounds object for physics" pass; if( bFlags & Blender.Object.RBFlags['MAINACTOR'] ): #Unused; requires ACTOR pass; if( bFlags & Blender.Object.RBFlags['CHILD'] ): #Unused; reserved pass; # #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 ): # global_x = global_max_aabb[0] - global_min_aabb[0]; global_y = global_max_aabb[1] - global_min_aabb[1]; global_z = global_max_aabb[2] - global_min_aabb[2]; # TShape = cGeoShape(); TShape.index = -1; TShape.name = ""; TShape.pointcenter = [0,0,0]; TShape.pointcenter[0] = global_x; TShape.pointcenter[1] = global_y; TShape.pointcenter[2] = global_z; TShape.pointmin = [ local_min_aabb[0], local_min_aabb[1], local_min_aabb[2] ]; TShape.pointmax = [ local_max_aabb[0], local_max_aabb[1], local_max_aabb[2] ]; TShape.params = []; # cshape = ""; if( bShape == Blender.Object.RBShapes.BOX ): cshape = "box"; TShape.type = "box"; # # "box" != "aabb" because aabb == world aligned; box = rotated # +X,+Y,+Z = scale of box # # Affected by scale (local aabb) # #Convert "box" definition & parameters to geo.h definitions #Convert local_aabb into a box. Easy enough (min/max) # #aabox = min, max (box) # #Non-aa boxes require full basis, & half-widths TShape.params.append( local_min_aabb[0] ); TShape.params.append( local_min_aabb[1] ); TShape.params.append( local_min_aabb[2] ); TShape.params.append( local_max_aabb[0] ); TShape.params.append( local_max_aabb[1] ); TShape.params.append( local_max_aabb[2] ); elif( bShape == Blender.Object.RBShapes.SPHERE ): cshape = "sphere"; TShape.type = "sphere"; # # bRadius - applies ignoring scale? No, scale does affect sphere (scaled sphere = ellipse) # +X,+Y,+Z = scale # #Convert "sphere" definition & parameters to geo.h definitions #Convert local_aabb into an aabb ellipse or sphere or axis-ellipse?. Easy enough (min/max) # #sphere = x,y,z,radius #aaellipse = min, max (fills box) # #Non-aa ellipses require full basis, & radii issphere = 0; if( nobj.scale[0] == nobj.scale[1] ): if( nobj.scale[0] == nobj.scale[2] ): issphere = 1; # if( issphere ): cshape = "sphere"; TShape.type = "sphere"; TShape.params.append( global_x ); TShape.params.append( global_y ); TShape.params.append( global_z ); TShape.params.append( bRadius ); else: cshape = "ellipse"; TShape.type = "ellipse"; #Need to define an ellipse # TShape.params.append( nobj.matrix[0] * nobj.scale[0] ); #X delta axis TShape.params.append( nobj.matrix[1] * nobj.scale[0] ); TShape.params.append( nobj.matrix[2] * nobj.scale[0] ); # TShape.params.append( nobj.matrix[3] * nobj.scale[1] ); #Y delta axis TShape.params.append( nobj.matrix[4] * nobj.scale[1] ); TShape.params.append( nobj.matrix[5] * nobj.scale[1] ); # TShape.params.append( nobj.matrix[6] * nobj.scale[2] ); #Z delta axis TShape.params.append( nobj.matrix[7] * nobj.scale[2] ); TShape.params.append( nobj.matrix[8] * nobj.scale[2] ); # TShape.params.append( global_x ); #Global center of ellipse TShape.params.append( global_y ); TShape.params.append( global_z ); elif( bShape == Blender.Object.RBShapes.CYLINDER ): cshape = "cylinder"; TShape.type = "cylinder"; # # +Z = cylinder direction # +X,+Y = width/height # #Convert "cylinder" definition & parameters to geo.h definitions #convert local_aabb into a cylinder as per blender definitions (f***) # #cylinder = x,y,z, vx,vy,vz, radius # #Note that cylinders have only 1 radius (Hm) so scale in +X, +Y must be identical (?hm) # #elliptical cylinders require "up" "left" "direction" and start position, as well as radii for up and left. iscylinder = 0; if( nobj.scale[0] == nobj.scale[1] ): iscylinder = 1; # if iscylinder: cshape = "cylinder"; TShape.type = "cylinder"; #Need to define an ellipse # TShape.params.append( global_x - ( nobj.matrix[0] * nobj.scale[2] / 2.0 ) ); #Blender cylinders are not appropriately centered TShape.params.append( global_y - ( nobj.matrix[1] * nobj.scale[2] / 2.0 ) ); TShape.params.append( global_z - ( nobj.matrix[2] * nobj.scale[2] / 2.0 ) ); # TShape.params.append( nobj.matrix[0] * nobj.scale[2] ); #X delta axis TShape.params.append( nobj.matrix[1] * nobj.scale[2] ); TShape.params.append( nobj.matrix[2] * nobj.scale[2] ); # TShape.params.append( nobj.scale[2] * (local_max_aabb[2] - local_min_aabb[2]) ); else: cshape = "ellipticalcylinder"; TShape.type = "ellipticalcylinder"; #Need to define an ellipse # TShape.params.append( nobj.matrix[0] * nobj.scale[0] ); #X delta axis TShape.params.append( nobj.matrix[1] * nobj.scale[0] ); TShape.params.append( nobj.matrix[2] * nobj.scale[0] ); # TShape.params.append( nobj.matrix[3] * nobj.scale[1] ); #Y delta axis TShape.params.append( nobj.matrix[4] * nobj.scale[1] ); TShape.params.append( nobj.matrix[5] * nobj.scale[1] ); # TShape.params.append( nobj.matrix[6] * nobj.scale[2] ); #Z delta axis TShape.params.append( nobj.matrix[7] * nobj.scale[2] ); TShape.params.append( nobj.matrix[8] * nobj.scale[2] ); # TShape.params.append( global_x ); #Global center of ellipse TShape.params.append( global_y ); TShape.params.append( global_z ); elif( bShape == Blender.Object.RBShapes.CONE ): cshape = "cone"; TShape.type = "cone"; # # +Y = tip of cone at end of AABB; # Y = scale of cone; -Y min = wide part # +X,+Z = scale of cone # #Convert "cone" definition & parameters to geo.h definitions #convert local_aabb into a cone as per blender definitions (f***) # #cone = x,y,z, vx,vy,vz, radius_start, radius_end; # #Scale cone # #elliptical cones require "up" "left" "direction" and start position, as well as start + end radii for up and left. iscone = 0; if( nobj.scale[0] == nobj.scale[2] ): iscone = 1; if( iscone ): #nobj.scale[1] = length TShape.params.append( 0 ); else: # TShape.params.append( 0 ); elif( bShape == Blender.Object.RBShapes.POLYHEDERON ): cshape = "poly"; TShape.type = "triangles"; # # ! This covers static poly-meshes and polyhedral hulls! #ERROR # #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? # #Can save a triangle list? # points (all unique points) (* x points per trismesh) # normals (facet normals) (1 x normal per triangle) # triangles (indicies) (3 x uints per triangle) TShape.params.append( 0 ); #whoa. how about 9 floats, 3 floats per each triangle? (inefficient, but complete) # #Add a link to this shape if needed? Hm. # #--Missing-- newLink = cObject.cALink(); newLink.name = ""; newLink.btype = obj.type; newLink.type = ""; nobj.btype = obj.type; #print nobj.name,obj.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' ): #Lamps have... #"type" mutual ( 'Lamp' 'Sun' 'Spot' 'Hemi' 'Area' 'Photon' ) #"mode" bitfield ( 'Shadows' 'Halo' 'Layer' 'Quad' 'Negative' 'OnlyShadow' 'Sphere' 'Square' 'NoDiffuse' 'NoSpecular' 'RayShadow' ) #"falloff" CONSTANT INVLINEAR INVSQUARE CUSTOM LINQUAD #"energy" -> no analog? #clip start/end -> ? effective distance? #RGB color emitted #newprop = cObject.cAProp(); #newprop.btype = 'FLOAT', #newprop.name = prop.getName(); #newprop.type = "float";#, "string"; #newprop.data = ("%d" % (prop.getData())); #nobj.properties.append( newprop ); #ERROR 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 = []; is_zoning = 0; zoningdata = ["","",""]; linkdata = ["","","",""]; for prop in nobj.properties: if( prop.type == "string" ): if( prop.name == "type" ): if( prop.data == "zone" ): #is a zone is_zoning = 1; elif( prop.data == "zplane" ): #is a zplane is_zoning = 2; #elif( prop.data == "****" ): #Other designations elif( prop.name == "link0" ): linkdata[0] = prop.data; elif( prop.name == "link1" ): linkdata[1] = prop.data; elif( prop.name == "link2" ): linkdata[2] = prop.data; elif( prop.name == "link3" ): linkdata[3] = prop.data; if( is_zoning == 1 ): #Zone name = zoningdata[0] #Physical zone data is IN OBJECT itself; IE "nobj" #Object must NOT be rotated (90 degree rotations OK?) #ERROR #That means for each row, SUM = +1 or -1, and two entries are ~0. #Looks like epsiolon: # ~8 x 10 ^ -8, 0.999999999997 #So, set epsilon to 1x10^-7 = 0.0000001 #nobj.matrix #Or, it should be identity... (100 010 001) #print "Zone:",nobj.name,nobj.matrix; #Global bounding box is used, AND must exist! if( global_minmax != None ): #so, nzone is: #name, #min position #max position #list of objects containted # ! hm. name, shared flag #list of zones collided with # ! hm. name of zone ZZ = cScene.cSZone(); ZZ.index = len( scened.zones ); ZZ.name = nobj.name; ZZ.minpos = [global_minmax[0], global_minmax[1], global_minmax[2]]; ZZ.maxpos = [global_minmax[3], global_minmax[4], global_minmax[5]]; ZZ.objs = []; ZZ.links = []; #print "Zone:",nobj.name,global_minmax; scened.zones.append( ZZ ); nobj_ignore_me = 1; else: print "#ERROR Zone: ", nobj.name, " no AABB! Check type/data"; elif( is_zoning == 2 ): #ZPlane name = zoningdata[0] #Physical plane data is IN OBJECT itself; IE "nobj" #Link1/From = zoningdata[1], object must exist #Link2/To = zoningdata[2], object must exist # #print "ZPlane: ", nobj.name, nobj.matrix; ZP = cScene.cSZplane(); ZP.index = len( scened.zplanes ); ZP.name = nobj.name; ZP.pos = nobj.pos; ZP.matrix = nobj.matrix; ZP.quat = None; ZP.size = nobj.scale; #! Might not be correct? Hm. ZP.links = []; #Must be exactly length 2, and both links must exist for s in linkdata: if( len(s) > 0 ): ZP.links.append( s ); if( len(ZP.links) == 2 ): scened.zplanes.append( ZP ); nobj_ignore_me = 1; else: print "#ERROR ZPlane: ", nobj.name, " not exactly 2 links: ",ZP.links; # #Add object if we are NOT ignoring it # if( nobj_ignore_me == 0 ): nobj.index = len(scened.objects); BDATA_objects_map[ nobj.name ] = nobj.index; scened.objects.append( nobj ); # #If this scene contains zones, repair/collect them (hm) # if( len( scened.zones ) > 0 ): #Build faster searching of objects HERE: Xsorted = []; O_index = 0; for O in scened.objects: if( O.minmax != None ): Xsorted.append( [ O.minmax[0], O_index ] ); else: #Some objects (like lights) have a center, and that matters. #print "X: ",O.name, O.btype, O.pos[0], O.pos[1], O.pos[2]; if( O.btype == 'Camera' ): O.minmax = [ O.pos[0], O.pos[1], O.pos[2], O.pos[0], O.pos[1], O.pos[2] ]; Xsorted.append( [ O.minmax[0], O_index ] ); elif( O.btype == 'Lamp' ): O.minmax = [ O.pos[0], O.pos[1], O.pos[2], O.pos[0], O.pos[1], O.pos[2] ]; Xsorted.append( [ O.minmax[0], O_index ] ); elif( O.btype == 'Text' ): #Lol, embed text objects as text properties? O.minmax = [ O.pos[0], O.pos[1], O.pos[2], O.pos[0], O.pos[1], O.pos[2] ]; Xsorted.append( [ O.minmax[0], O_index ] ); O_index += 1; Xsorted.sort( eSortByFirstElement ); Xsorted_len = len( Xsorted ); #Go check against ALL objects (slow, but works) & add em' map_zzones = {}; Z_index = 0; for Z in scened.zones: addobjs = []; #Ensure correct mapping & indexing! Z.index = Z_index; map_zzones[ Z.name ] = Z.index; Z_index += 1; #find (binary) min start position? Aw screw it. minpcX = Z.minpos[0]; maxpcX = Z.maxpos[0]; Oss_found = []; Oss_mode = 0; for Oss in Xsorted: if( Oss_mode != 1 ): if( Oss[0] > maxpcX ): break; else: Oss_found.append( Oss[1] ); else: if( Oss[0] >= minpcX ): Oss_mode = 1; if( Oss[0] > maxpcX ): break; else: Oss_found.append( Oss[1] ); #print "Zone: ",Z.name; #Start at position & check AABB hits haslist = []; minpcY = Z.minpos[1]; maxpcY = Z.maxpos[1]; minpcZ = Z.minpos[2]; maxpcZ = Z.maxpos[2]; for Oci in Oss_found: OC = scened.objects[ Oci ]; #AABB test: if( (OC.minmax[0] < maxpcX) and (OC.minmax[3] > minpcX) ): if( (OC.minmax[1] < maxpcY) and (OC.minmax[4] > minpcY) ): if( (OC.minmax[2] < maxpcZ) and (OC.minmax[5] > minpcZ) ): #Contained! #Well, actually just the GLOBAL AABB is containted. #Implement a more rigorous check here, for some shapes #(IE use local AABB of object, if present, and check a transformed box against a AABB for the zone) # # -??? #ERROR #ERROR haslist.append( [ 0, OC.name ] ); #print "\tHas: ",OC.name; #Assign list: Z.objs = haslist; #Yeilding if( eInstantQuit() ): GLOBAL_QUIT = 1; return; #Now determine zone-zone collisions: for Z in scened.zones: minpcX = Z.minpos[0]; maxpcX = Z.maxpos[0]; minpcY = Z.minpos[1]; maxpcY = Z.maxpos[1]; minpcZ = Z.minpos[2]; maxpcZ = Z.maxpos[2]; #print "Zone: ",Z.name; haszones = []; for Z2Z in scened.zones: if( Z2Z.index != Z.index ): if( ( Z2Z.minpos[0] < maxpcX) and ( Z2Z.maxpos[0] > minpcX) ): if( (Z2Z.minpos[1] < maxpcY) and (Z2Z.maxpos[1] > minpcY) ): if( (Z2Z.minpos[2] < maxpcZ) and (Z2Z.maxpos[2] > minpcZ) ): haszones.append( Z2Z.name ); #print "\tLink: ",Z2Z.name; #Now determine shared object regions: #(All shared counts are 0; do this one-way since it's quadratic) # for zton in Z.objs: for zss2 in Z2Z.objs: if( zton[1] == zss2[1] ): zton[0] += 1; zss2[0] += 1; #Yeilding if( eInstantQuit() ): GLOBAL_QUIT = 1; return; Z.links = haszones; #Yeilding if( eInstantQuit() ): GLOBAL_QUIT = 1; return; #Time to check planes: if( len( scened.zplanes ) > 0 ): for ZP in scened.zplanes: #print "ZPlane: ",ZP.name; reallinks = []; for ss in ZP.links: reallinks.append( ss ); if ss in map_zzones: #OK, convert to index. #print "\tLink: ",ss; pass; else: # print "#WARNING ZPlane: ",ZP.name," -> ",ss," Zone does not exist locally!"; if( len(ZP.links) == 2 ): ZP.links = reallinks; else: ZP.links = []; print "#ERROR ZPlane: ",ZP.name," Not exactly 2 links!"; else: print "#WARNING ZPlanes: No planes!"; #Attempt to auto-generate Z-planes? LOL! ha ha ha ha. How? # # -> Might be able to generate a plane at least, but it won't be pretty. else: if( len( scened.zplanes ) > 0 ): print "#ERROR ZPlanes: No zones!"; 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_USE_JSON; 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( "\n" ); XMF.push( "ted" ); XMF.writeline( "name", GLOBAL_FILE_NAME ); XMF.writeline( "date", debug_time_date ); XMF.writeline( "author", __version__ ); #Write the coordinate definition this exporter uses XMF.push( "coordinates" ); XMF.writeline( "name", "universal" );#logical colloquial name XMF.writeline( "handedness", "right" );#logical handedness XMF.writeline( "basis", [1,0,0,0,1,0,0,0,1] );#Forward left up vector basis XMF.writeline( "coordinate", [0,1,2] );#x,y,z indicies in memory for vectors XMF.writeline( "quaternion", [0,1,2,3] );#x,y,z,w indicies in memory for quaternions XMF.writeline( "matrix", "XxXyXzYxYyYzZxZyZz" );#0,1,2,3,4,5,6,7,8 memory components and their syntactical meaning in relation to the basis vectors (X => Xx Xy Xz, ect...) XMF.writeline( "units", "meters" );#Units of measurment; 1 unit = this logical unit XMF.writeline( "scale", [1,1,1] );#Scaling per axis of this file's coordinates XMF.pop( "coordinates" ); #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 ); i_count = 0; for carma in BDATA_armatures: if( GLOBAL_USE_JSON ): XMF.push( "armature"+str(i_count) ); else: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 ); j_count = 0; for bone in carma.bones: if( GLOBAL_USE_JSON ): XMF.push( "bone"+str(j_count) ); else: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 ); if( GLOBAL_USE_JSON ): XMF.pop( "bone"+str(j_count) ); else:XMF.pop( "bone" ); j_count += 1; 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 ); j_count = 0; 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 ): if( GLOBAL_USE_JSON ): XMF.push( "ik_chain"+str(j_count) ); else: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 ); if( GLOBAL_USE_JSON ): XMF.pop( "ik_chain"+str(j_count) ); else:XMF.pop( "ik_chain" ); j_count += 1; XMF.pop( "ik_chains" ); #Spline points len_splinepoints = len( carma.splinepoints ); if( len_splinepoints > 0 ): XMF.push( "splinepoints" ); XMF.writeline( "count", len_splinepoints ); j_count = 0; for sp in carma.splinepoints: if( GLOBAL_USE_JSON ): XMF.push( "splinepoint"+str(j_count) ); else: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 ] ); if( GLOBAL_USE_JSON ): XMF.pop( "splinepoint"+str(j_count) ); else:XMF.pop( "splinepoint" ); j_count += 1; 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" ); if( GLOBAL_USE_JSON ): XMF.pop( "armature"+str(i_count) ); else:XMF.pop( "armature" ); i_count+=1; 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 ); i_count = 0; for cchan in BDATA_channels: if( eInstantQuit() ): GLOBAL_QUIT = 1; return; if( GLOBAL_USE_JSON ): XMF.push( "channel"+str(i_count) ); else: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" ); len_kscripts = len( cchan.key_scripts ); if( len_kscripts > 0 ): XMF.push( "keyscripts" ); XMF.writeline( "count", len_kscripts ); XMF.push( "keytimes" ); for kscript in cchan.key_scripts: XMF.writearraydata( [kscript.time - cchan.time_start], 1 ); XMF.pop( "keytimes" ); XMF.push( "scripts" ); j_count = 0; for kscript in cchan.key_scripts: if( GLOBAL_USE_JSON ): XMF.writeline( "script" + str(j_count), "\n" + kscript.script ); else:XMF.writeline( "script", "\n" + kscript.script ); #XMF.push( "script" ); #Write some array data? Hm. # #Replace all \n with ';' ? Hm. # #XMF.writearraydata( [kscript.script], 1 ); #XMF.pop( "script" ); j_count += 1; XMF.pop( "scripts" ); XMF.pop( "keyscripts" ); if( GLOBAL_USE_JSON ): XMF.pop( "channel"+str(i_count) ); else:XMF.pop( "channel" ); i_count += 1; 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 ); i_count = 0; for canim in BDATA_animations: if( eInstantQuit() ): GLOBAL_QUIT = 1; return; if( GLOBAL_USE_JSON ): XMF.push( "animation"+str(i_count) ); else: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" ); j_count = 0; for blch in canim.blendchannels: if( GLOBAL_USE_JSON ): XMF.push( "blendchannel"+str(j_count) ); else: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" ); if( GLOBAL_USE_JSON ): XMF.pop( "blendchannel"+str(j_count) ); else:XMF.pop( "blendchannel" ); j_count += 1; #XMF.pop( "data" ); if( GLOBAL_USE_JSON ): XMF.pop( "animation"+str(i_count) ); else:XMF.pop( "animation" ); i_count += 1; 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 ); i_count = 0; 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 if( GLOBAL_USE_JSON ): XMF.push( "mesh"+str(i_count) ); else: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 ); j_count = 0; for v in mesh.va_weights: vwlen = len( v.weights ); vilen = len( v.weight_indicies ); vnlen = len( v.weight_names ); if( GLOBAL_USE_JSON ): XMF.push( "weightset"+str(j_count) ); else: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 k_count = 0; for V in v.weight_names: if( GLOBAL_USE_JSON ): XMF.writeline( "name" + str(k_count), V ); else: XMF.writeline( "name", V ); k_count += 1; 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!"; if( GLOBAL_USE_JSON ): XMF.pop( "weightset"+str(j_count) ); else:XMF.pop( "weightset" ); j_count += 1; 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 ); j_count = 0; for ggroup in mesh.va_groups: if( GLOBAL_USE_JSON ): XMF.push( "vertgroup"+str(j_count) ); else:XMF.push( "vertgroup" ); XMF.writeline( "name", ggroup.name ); #Calculate bounds (?) (hard?) #float float float float float float 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"); if( GLOBAL_USE_JSON ): XMF.pop( "vertgroup"+str(j_count) ); else:XMF.pop( "vertgroup" ); j_count += 1; XMF.pop( "vertgroups" ); XMF.commit(); #Polygroups len_polygroups = len( mesh.ia_groups ); if( len_polygroups > 0 ): XMF.push( "polygroups" ); XMF.writeline( "count", len_polygroups ); j_count = 0; for NG in mesh.ia_groups: if( GLOBAL_USE_JSON ): XMF.push( "polygroup"+str(j_count) ); else: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 = []; k_count = 0; for wv in NG.matrixpalette : if( GLOBAL_USE_JSON ): XMF.push( "matrixlink"+str(k_count) ); else: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 ); if( GLOBAL_USE_JSON ): XMF.pop( "matrixlink"+str(k_count) ); else:XMF.pop( "matrixlink" ); k_count += 1; 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) ); k_count = 0; for tarelm in tardata: if( GLOBAL_USE_JSON ): XMF.push( "link"+str(k_count) ); else:XMF.push( "link" ); XMF.writeline( "name", tarelm[0] ); XMF.writeline( "data", tarelm[1] ); if( GLOBAL_USE_JSON ): XMF.pop( "link"+str(k_count) ); else:XMF.pop( "link" ); k_count += 1; 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" ); if( GLOBAL_USE_JSON ): XMF.pop( "polygroup"+str(j_count) ); else:XMF.pop( "polygroup" ); j_count += 1; XMF.pop( "polygroups" ); if( GLOBAL_USE_JSON ): XMF.pop( "mesh"+str(i_count) ); else:XMF.pop( "mesh" ); i_count += 1; 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 ); i_count = 0; for T in BDATA_textures: if( eInstantQuit() ): GLOBAL_QUIT = 1; return; if( GLOBAL_USE_JSON ): XMF.push( "texture"+str(i_count) ); else: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" ); if( GLOBAL_USE_JSON ): XMF.pop( "texture"+str(i_count) ); else:XMF.pop( "texture" ); i_count += 1; 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 ); i_count = 0; for matdata in BDATA_materials: if( eInstantQuit() ): GLOBAL_QUIT = 1; return; if( GLOBAL_USE_JSON ): XMF.push( "material"+str(i_count) ); else: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 ); j_count = 0; for texname in matdata.textures: if( GLOBAL_USE_JSON ): XMF.push( "texture"+str(j_count) ); else:XMF.push( "texture" ); XMF.writeline( "name", texname ); XMF.writeline( "blend", 0 ); if( GLOBAL_USE_JSON ): XMF.pop( "texture"+str(j_count) ); else:XMF.pop( "texture" ); j_count += 1; XMF.pop( "textures" ); #self.specularity = 32; #specular highlight 'size' #self.shaders = []; #List of shaders (by name) if( GLOBAL_USE_JSON ): XMF.pop( "material"+str(i_count) ); else:XMF.pop( "material" ); i_count += 1; 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 ); i_count = 0; for modeldata in BDATA_models: if( eInstantQuit() ): GLOBAL_QUIT = 1; return; if( GLOBAL_USE_JSON ): XMF.push( "model"+str(i_count) ); else:XMF.push( "model" ); XMF.writeline( "name", matdata.name ); if( GLOBAL_USE_JSON ): XMF.pop( "model"+str(i_count) ); else: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; i_count = 0; for cscene in BDATA_scenes: if( eInstantQuit() ): GLOBAL_QUIT = 1; return; if( GLOBAL_USE_JSON ): XMF.push( "scene"+str(i_count) ); else: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 ); j_count = 0; for obj in cscene.objects: if( GLOBAL_USE_JSON ): XMF.push( "object"+str(j_count) ); else: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 ); k_count = 0; for proppy in obj.properties: if( GLOBAL_USE_JSON ): XMF.push( "value"+str(k_count) ); else:XMF.push( "value" ); XMF.writeline( "name", proppy.name ); XMF.writeline( "type", proppy.type ); XMF.writeline( "data", proppy.data ); if( GLOBAL_USE_JSON ): XMF.pop( "value"+str(k_count) ); else:XMF.pop( "value" ); k_count += 1; XMF.pop( "values" ); len_links = len( obj.links ); if( len_links > 0 ): XMF.push( "links" ); XMF.writeline( "count", len_links ); k_count = 0; for linky in obj.links: if( GLOBAL_USE_JSON ): XMF.push( "link"+str(k_count) ); else:XMF.push( "link" ); XMF.writeline( "name", linky.name ); XMF.writeline( "type", linky.type ); if( GLOBAL_USE_JSON ): XMF.pop( "link"+str(k_count) ); else:XMF.pop( "link" ); k_count += 1; XMF.pop( "links" ); if( GLOBAL_USE_JSON ): XMF.pop( "object"+str(j_count) ); else:XMF.pop( "object" ); j_count += 1; XMF.commit(); XMF.pop( "objects" ); """ zoning zones count uint 1 zone name string min float 3 max float 3 objects count uint 1 object name string shared uint 1 links count uint 1 name string zplanes count uint 1 zplane name string position float 3 matrix float 9 quaternion float 4 size float 3 from string to string """ len_zones = len( cscene.zones ); len_zplanes = len( cscene.zplanes ); if( len_zones > 0 or len_zplanes > 0 ): XMF.push( "zoning" ); if( len_zones > 0 ): XMF.push( "zones" ); XMF.writeline( "count", len_zones ); j_count = 0; for Z in cscene.zones: if( GLOBAL_USE_JSON ): XMF.push( "zone"+str(j_count) ); else:XMF.push( "zone" ); XMF.writeline( "name", Z.name ); XMF.writeline( "min", Z.minpos ); XMF.writeline( "max", Z.maxpos ); len_zobjects = len( Z.objs ); if( len_zobjects > 0 ): XMF.push( "objects" ); XMF.writeline( "count", len_zobjects ); k_count = 0; for obl in Z.objs: if( GLOBAL_USE_JSON ): XMF.push( "object"+str(k_count) ); else:XMF.push( "object" ); XMF.writeline( "name", obl[1] ); if( obl[0] != 0 ): XMF.writeline( "shared", obl[0] ); #!! what?? if( GLOBAL_USE_JSON ): XMF.pop( "object"+str(k_count) ); else:XMF.pop( "object" ); XMF.pop( "objects" ); len_zlinks = len( Z.links ); if( len_zlinks > 0 ): XMF.push( "links" ); XMF.writeline( "count", len_zlinks ); k_count = 0; for obl in Z.links: if( GLOBAL_USE_JSON ): XMF.writeline( "name" + str(k_count), obl ); else:XMF.writeline( "name", obl ); k_count += 1; XMF.pop( "links" ); if( GLOBAL_USE_JSON ): XMF.pop( "zone"+str(j_count) ); else:XMF.pop( "zone" ); j_count += 1; XMF.pop( "zones" ); # if( len_zplanes > 0 ): XMF.push( "zplanes" ); XMF.writeline( "count", len_zplanes ); j_count = 0; for Z in cscene.zplanes: if( len( Z.links ) == 2 ): if( GLOBAL_USE_JSON ): XMF.push( "zplane"+str(j_count) ); else:XMF.push( "zplane" ); XMF.writeline( "name", Z.name ); XMF.writeline( "position", Z.pos ); if( Z.matrix != None ): XMF.writeline( "matrix", Z.matrix ); if( Z.quat != None ): XMF.writeline( "quaternion", Z.quat ); XMF.writeline( "size", Z.size ); # #ERROR - needs from / to ??? XMF.writeline( "from", Z.links[0] ); XMF.writeline( "to", Z.links[1] ); if( GLOBAL_USE_JSON ): XMF.pop( "zplane"+str(j_count) ); else:XMF.pop( "zplane" ); j_count += 1; XMF.pop( "zplanes" ); XMF.pop( "zoning" ); if( GLOBAL_USE_JSON ): XMF.pop( "scene"+str(i_count) ); else:XMF.pop( "scene" ); i_count += 1; 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" ); # #Convert the DXML file into a JSON object; # What can we do about it? # if( GLOBAL_USE_JSON ): eDisplayProgressBar( 0.98, "JSON Convert..." ); global GLOBAL_JSON_DEBUG; GLOBAL_JSON_DEBUG = 0; # #Lamp #0 #20.0 10.0 10.0 # #0.6634 -0.6782 -0.3163 #0.3148 0.6363 -0.7043 #0.6789 0.3676 0.6356 # #1.0 1.0 1.0 # #2097152 #0123456789:;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
#2796203 #ANYTHING but the " #JSON is like # {"DolKoopaR1":{"type":"Mesh","parent":"None","position":[0.0,0.0,0.0],"mass":1.0,"box":[[-0.66724395752,-0.97254049778,-0.757191956043],[1.07083463669,0.872469305992,0.947803676128]],"rotation":[0.0,0.0,0.0],"scale":[1.0,1.0,1.0],"drawType":4,"vertex":{"0":[ # #{ # "keyname":{ # "keyname":"value" # "keyname":"value" # } # "OBJECT":{ # "nest":{ # # } # "nest2":{ # # } # } #} # #JSON has to remove all formatting whitespace ('\n' '\t' '\r') for spaces. This is still within DXML ted specs. #However, you can't replucate the codes. # # #Essentially a parse converter: # #XMF.finaldata X = XMF.finaldata; i = 0; parstate = 0; prevstate = 0; i_final = len( X ); i_kill = 0; tagstack = []; dataelements = []; dataresult = []; datastringy = 0; netfile = []; while( not i_kill ): # #Find specific sequences based on rules: # #'<' tightstring + '>' = push( tightstring ) #'' = pop( tightstring ) == top() #Anything else that has characters BESIDES whitespace is data. # Data -> Numeric list (0123456789.+-) # Data -> String (exact) is anything else # #RESTRICTION: the first character of data cannot be '<', the data cannot contain the sequence '' (push) tagfull = ""; i+=1; while( i < i_final ): c = X[i]; if( c == '>' ): i += 1; break; else: tagfull += c; i += 1; #May be a ' 0 ): FPS = ""; if( datastringy ): #String data! Write a " enclosed string. Could process it for bad characters (JSON will not accept \n,\r,\t characters. RPS = ""; for s in dataelements: RPS += s; for c in RPS: if( c == '\n' ): FPS += "\\n"; elif( c == '\r' ): FPS += "\\r"; elif( c == '\t' ): FPS += "\\t"; elif( c == ' ' ): FPS += ' '; elif( c == '\\' ): FPS += "\\\\"; #Apparently, a stray '\' fucks it up hugely. DXML documents don't have this problem. else: FPS += c; FPS = "\"" + FPS + "\""; else: #Numeric array! LPS = []; if( len( dataelements ) == 1 ): LPS = list(dataelements[0]); else: LPS += list("["); for e in dataelements: LPS += list(e + ","); LPS[ (len(LPS) - 1) ] = ']'; FPS = "".join(LPS); #FPS is the pure data string. if( len( FPS ) > 0 ): #Add it to the file (hm) netfile.append( [2,FPS] ); #"write" data #Erase it dataelements = []; datastringy = 0; else: #Add tabs as needed (hm!) #R += list("\t"); #for v in tagstack: # R += list("\t"); #R += list("},"); #R += list("\n"); pass; netfile.append( [0,tagclip] ); #"pop" tag else: print "JSON: Bad tag pop!"; i_kill = 1;##ERROR else: #Complete previous tag write (add the '{\n' character) #Write tab spaces #for v in tagstack: # R += list("\t"); #Write tag #R += list("\"" + tagfull + "\":"); tagstack.append( tagfull ); netfile.append( [1,tagfull] ); #"push" tag #i is now AFTER tag. so search for data. elif( ctype == 0 ): #Just whitespace i += 1; else: #NOT whitespace, must be DATA! #Read until " 0 ): #Check for other numeric problems. like multiple symbols (.., --, ++). #Only the first character of a number can be +-. #Only 1 period per word. y = 0; y_final = len( datastring ); if( (datastring[0] == '-') or (datastring[0] == '+') ): y = 1; #Still valid. if( y_final < 2 ): datastringy = 1; break; pcount = 0; while( y < y_final ): yc = datastring[y]; if( yc >= '0' and yc <= '9' ): #ONLY numerals. pass; elif( yc == '.' ): #ONLY 1 period allowed pcount+= 1; if( pcount > 1 ): datastringy = 1; break; else: datastringy = 1; break; y += 1; datastring = ""; else: datastring += c; netstring += c; i += 1; #Final check if( len( datastring ) > 0 ): #Check for other numeric problems. like multiple symbols (.., --, ++). #Only the first character of a number can be +-. #Only 1 period per word. y = 0; y_final = len( datastring ); if( (datastring[0] == '-') or (datastring[0] == '+') ): y = 1; #Still valid. if( y_final < 2 ): datastringy = 1; if( datastringy == 0 ): pcount = 0; while( y < y_final ): yc = datastring[y]; if( yc >= '0' and yc <= '9' ): #ONLY numerals. pass; elif( yc == '.' ): #ONLY 1 period allowed pcount+= 1; if( pcount > 1 ): datastringy = 1; break; else: datastringy = 1; break; y += 1; dataelements = []; #Process netstring if needed if( datastringy ): #string! dataelements.append( netstring ); else: #numeric array, process it: datastring = ""; for c in netstring: ctype = eCharType( c ); if( ctype == 0 ): if( len( datastring ) > 0 ): dataelements.append( datastring ); datastring = ""; else: datastring += c; if( len( datastring ) > 0 ): dataelements.append( datastring ); if( i >= i_final ): i_kill = 1; R = []; R += list("{"); x = 0; x_final = len( netfile ); xstack = []; prevtype = 0; while( x < x_final ): xtype0 = netfile[x][0]; xdata0 = netfile[x][1]; xtype1 = xtype0; #Must be a pop if at end. rtype = xtype0; if( x+1 < x_final ): xtype1 = netfile[x+1][0]; else: rtype = 0; #If current == 0, pop tag. #If current == 1, add tag. #If current == 2, add data. if( rtype == 0 ): #was data or was element? Hm. if( len( xstack ) > 0 ): if( xstack[ len(xstack)-1 ] == 0 ): if( GLOBAL_JSON_DEBUG ): #Endl (debugging) R += list("\n"); #Tabs (debugging) for v in xstack: R += list("\t"); R += list("}"); #Remove it from the stack xstack.pop(); #End of elements if( xtype1 != 0 ): R += list(","); elif( rtype == 1 ): #Hm. Accurate. if( prevtype != 0 ): R += list("{"); if( xtype1 == 2 ): xstack.append( 1 ); #data else: xstack.append( 0 ); #element if( GLOBAL_JSON_DEBUG ): #Endl (debugging) R += list("\n"); #Tabs (debugging) for v in xstack: R += list("\t"); #Write tag name out R += list("\"" + xdata0 + "\":"); #IF next is a pop (empty tag) make sure it works: if( xtype1 == 0 ): R += list("{"); elif( rtype == 2 ): #Write data out! R += list(xdata0); prevtype = rtype; x += 1; R += list("}"); eDisplayProgressBar( 0.98, "Write File..." ); # #The DXML we are exporting objects DXML rules. # # txt.write( "".join(R) ); else: 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( "*/\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. #can also write code for the new DXML loader syntax: log.write( "dxml::loader & DXL = yourdxmlloader;\n" ); log.write( "DXL.sync_clear();\n\n" ); for X in Etree: stot = "DXL.sync( \""; for tt in X[2]: stot += tt + "/"; stot = stot[:-1]; stot += "\""; log.write( stot + ", "+X[0]+");\n" ); #str(X[1]) log.write( "\n\n" ); #and can write a big fat enum: log.write( "switch( ){\n" ); for X in Etree: log.write( "\tcase "+X[0]+":\n" ); #str(X[1]) log.write( "\t\tbreak;\n" ); #str(X[1]) log.write( "\tdefault:\nbreak;\n" ); log.write( "}\n\n" ); # #---------------------------------------------------------------------- # #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_USE_TEXTURES; 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_USE_JSON; 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_USE_TEXTURES = rdict['global_use_textures']; except: GLOBAL_USE_TEXTURES = 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.Get('filename'); #Remove ".blend" and everything before and including the first '/' pds = GLOBAL_DATAGROUP_NAME.rfind("\\"); if( pds >= 0 ): GLOBAL_DATAGROUP_NAME = GLOBAL_DATAGROUP_NAME[pds:-1]; pdi = GLOBAL_DATAGROUP_NAME.rfind("."); if( pdi > 0 ): GLOBAL_DATAGROUP_NAME = GLOBAL_DATAGROUP_NAME[1:pdi]; try: GLOBAL_USE_JSON = rdict['global_use_fjon']; except: GLOBAL_USE_JSON = False; try: GLOBAL_CONVERT_QUADS; except: GLOBAL_CONVERT_QUADS = True; #else: # #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_TEXTURES = eDisplayCreate( GLOBAL_USE_TEXTURES ); 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_USE_JSON = eDisplayCreate( GLOBAL_USE_JSON ); 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'),\ ('Use Textures', GUI_USE_TEXTURES, 'Textures are converted to text and exported in the file'),\ ('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'),\ ('Convert To JSON', GUI_USE_JSON, 'Changes the DXML to JSON on output'),\ ]; # #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_USE_TEXTURES = GUI_USE_TEXTURES.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_USE_JSON = GUI_USE_JSON.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_use_textures':GLOBAL_USE_TEXTURES, '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_use_json':GLOBAL_USE_JSON, '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_use_textures':GLOBAL_USE_TEXTURES, '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_use_json':GLOBAL_USE_JSON, '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( "" );