#
# $Id: alcdraw.py 477 2006-04-30 15:48:27Z AdamJohnso $
#
#    Copyright (C) 2005-2006  Alcugs pyprp Project Team
#    See the file AUTHORS for more info about the team
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    Please see the file COPYING for the full license.
#    Please see the file DISCLAIMER for more details, before doing nothing.
#

try:
    import Blender
    try:
        from Blender import NMesh
    except Exception, detail:
        print detail
except ImportError:
    pass

import array, random, binascii
from alcurutypes import *
from alcurugeom import *
from alcConvexHull import *
from alcGObjects import *
from alc_AbsClasses import *
import alcconfig

class VertexCoderElement:
    def __init__(self,base=0,count=0):
        self.base = base #FLOAT
        self.count = count #WORD
    
    
    def read(self,buf,granularity):
        if (self.count == 0):
            self.base = buf.ReadFloat()
            self.count = buf.Read16()
        if (self.count != 0):
            self.count = self.count - 1
            return (self.base + granularity * buf.Read16())
        raise RuntimeError, "VertexCoderElement prematurely reached end of count"

class VertexCoderColorElement:
    def __init__(self,base=0,count=0):
        self.base = base
        self.count = count
        self.RLE = None #Bool
    
    
    def read(self,buf):
        if (self.count == 0):
            self.count = buf.Read16()
            if ((self.count & 0x8000) != 0):
                self.RLE = 1
                self.base = buf.ReadByte()
                self.count &= 0x7FFF
            else:
                self.RLE = 0
        if (self.count != 0):
            self.count = (self.count - 1)
            if self.RLE:
                return self.base
            else:
                return buf.ReadByte()
        else:
           raise RuntimeError, "VertexCoderColorElement prematurely reached end of count."


class plVertexCoder:
    def __init__(self):
        #State vars
        self.Elements=[]
        for i in range(30):
            self.Elements.append(VertexCoderElement())
        self.Colors=[]
        for i in range(4):
            self.Colors.append(VertexCoderColorElement())
    
    def read(self,bufferGroup,buf,count):
        #read encoded vertex info
        for i in range(count):
            #Vertex Coordinates
            vert=Vertex()
            vert.x=self.Elements[0].read(buf,1.0/1024.0)
            vert.y=self.Elements[1].read(buf,1.0/1024.0)
            vert.z=self.Elements[2].read(buf,1.0/1024.0)
            #Vertex weights
            for e in range(bufferGroup.NumBlendWeights):
                vert.blend.append(self.Elements[3+e].read(buf,1.0/32768.0))
            if bufferGroup.NumBlendWeights and bufferGroup.blend:
                b1,b2,b3,b4,=struct.unpack("BBBB",buf.read(4)) #4 bones
                vert.bones=[b1,b2,b3,b4] #absolute bone index
            #normals
            vert.nx = (buf.Read16() / 32767.0)
            vert.ny = (buf.Read16() / 32767.0)
            vert.nz = (buf.Read16() / 32767.0)
            #colors
            vert.color.b = self.Colors[0].read(buf)                 
            vert.color.g = self.Colors[1].read(buf)                 
            vert.color.r = self.Colors[2].read(buf)                 
            vert.color.a = self.Colors[3].read(buf)                 
            #texture coordinates
            for j in range(bufferGroup.ntex):
                tex=[]
                tex.append(self.Elements[6+(3*j)].read(buf,1.0/65536.0))
                tex.append(self.Elements[7+(3*j)].read(buf,1.0/65536.0))
                tex.append(self.Elements[8+(3*j)].read(buf,1.0/65536.0))
                vert.tex.append(tex)
            bufferGroup.VertexStorage.append(vert)

    
    def write(self,bufferGroup,buf,count):
        #State vars
        VarA=[] #vertex coords (3)
        Vector=[] #real vector
        VBase=[] #vertex coords base
        offset=[] #offsets
        xVector=[]
        VarB=[] #vertex weights (nblends)
        BBase=[] #weight base
        VarC=[] #vertex colors RGBA (4)
        color=[] #the last color
        VarD=[] #vertex tex coords (3 * ntex)
        TBase=[] #tex coords base
        for i in range(3):
            VarA.append(0)
            Vector.append(0)
            xVector.append(0)
            VBase.append(0)
            offset.append(0)
        for i in range(bufferGroup.NumBlendWeights):
            VarB.append(0)          
            BBase.append(0)
        for i in range(4):
            VarC.append(0)
            color.append(0)
        for i in range(3*bufferGroup.ntex):
            VarD.append(0)
            TBase.append(0)
        compress=alcconfig.vertex_compression
        #write encoded vertex info
        for i in range(count):
            vert=bufferGroup.VertexStorage[i]
            #Vertex Coordinates
            Vector[0]=vert.x
            Vector[1]=vert.y
            Vector[2]=vert.z
            if not compress:
                #uncompressed
                for e in range(3):
                   buf.write(struct.pack("fHH",Vector[e],1,0)) #vertex base, n_vertexs, offset
            else:
                #compressed
                for e in range(3):
                    if VarA[e]==0:
                        #first vertex with new base
                        VBase[e]=Vector[e]
                        #count vertex with same base
                        for vi in range(n-i):
                            xxvert = bufferGroup.VertexStorage[vi+i]
                            xVector[0]=xxvert.x
                            xVector[1]=xxvert.y
                            xVector[2]=xxvert.z
                            off = (xVector[e]-VBase[e]) * 1024.0
                            #32768
                            if off<65536 and off>=0:
                                VarA[e]=VarA[e]+1
                            else:
                                break
                        buf.write(struct.pack("fH",VBase[e],VarA[e]))
                    off = (Vector[e]-VBase[e]) * 1024.0
                    buf.write(struct.pack("H",off))
                    VarA[e]=VarA[e]-1

            #Vertex weights
            if not compress:
                for e in range(bufferGroup.NumBlendWeights):
                    buf.write(struct.pack("fHH",vert.blend[e],1,0)) #blend base, n, offset
            else:
                for e in range(bufferGroup.NumBlendWeights):
                    if VarB[e]==0:
                        #first
                        BBase[e]=vert.blend[e]
                        #count
                        for vi in range(n-i):
                            xxvert = bufferGroup.VertexStorage[vi+i]
                            off = (xxvert.blend[e] - BBase[e]) * 32768.0
                            if off<65536 and off>=0:
                                VarB[e]=VarB[e]+1
                            else:
                                break
                        buf.write(struct.pack("fH",BBase[e],VarB[e]))
                    off = (vert.blend[e] - BBase[e]) * 32768.0
                    buf.write(struct.pack("H",off))
                    VarB[e]=VarB[e]-1
            if bufferGroup.NumBlendWeights and bufferGroup.blend:
                buf.write(struct.pack("BBBB",vert.bones[0],vert.bones[1],vert.bones[2],vert.bones[3]))
            #normals
            nx = int(vert.nx * 32768.0)
            ny = int(vert.ny * 32768.0)
            nz = int(vert.nz * 32768.0)
            if nx > 32767:
                nx = 32767
            elif nx < -32767:
                nx = -32767
            if ny > 32767:
                ny = 32767
            elif ny < -32767:
                ny = -32767
            if nz > 32767:
                nz = 32767
            elif nz < -32767:
                nz = -32767
            try:
                buf.write(struct.pack("hhh",nx,ny,nz))
            except:
                raise "wtf %i %i %i, %i %i %i" %(vert.nx,vert.ny,vert.nz,nx,ny,nz)
            #colors
            #vcolor=vert.color.uget()
            vcolor=[vert.color.b,vert.color.g,vert.color.r,vert.color.a]
            if not compress:
                for e in range(4):
                    buf.write(struct.pack("H",1))
                    buf.write(struct.pack("B",vcolor[e]))
            else:
                for e in range(4):
                    if VarC[e]==0:
                        #first
                        color[e]=vcolor[e]
                        #count
                        for vi in range(n-i):
                            vicolor=[bufferGroup.VertexStorage[vi+i].color.b,bufferGroup.VertexStorage[vi+i].color.g,bufferGroup.VertexStorage[vi+i].color.r,bufferGroup.VertexStorage[vi+i].color.a]
                            if vicolor[e]==color[e]:
                                VarC[e]=VarC[e]+1
                            else:
                                break
                        if VarC[e]==1:
                            cf=0
                        else:
                            cf=0x8000
                        buf.write(struct.pack("HB",VarC[e] | cf,color[e]))
                    VarC[e]=VarC[e]-1
            #texture coordinates
            if not compress:
                for j in range(bufferGroup.ntex):
                    for k in range(3):
                        buf.write(struct.pack("fHH",vert.tex[j][k],1,0)) #base, n, offset
                        #buf.write(struct.pack("fHH",0,1,0))
            else:
                for j in range(bufferGroup.ntex):
                    for k in range(3):
                        e=(3*j)+k
                        if VarD[e]==0:
                            #first
                            TBase[e]=vert.tex[j][k]
                            #count
                            for vi in range(n-i):
                                xbase=bufferGroup.VertexStorage[vi+i].tex[j][k]
                                off = (xbase - TBase[e]) * 65536.0
                                if off<65536 and off>=0:
                                    VarD[e]=VarD[e]+1
                                else:
                                    break
                            buf.write(struct.pack("fH",TBase[e],VarD[e]))
                        #print vert.tex[j][k],TBase[e]
                        off=(vert.tex[j][k]-TBase[e]) * 65536.0
                        #assert((TBase[e]+(off/65536.0))==vert.tex[j][k])
                        #print i,j,k,e,off
                        #vert.dump()
                        buf.write(struct.pack("H",off))
                        VarD[e]=VarD[e]-1


class plGBufferCell:
    def __init__(self):
        self.VertexIndex = 0 #DWORD
        self.ColorIndex = -1 #DWORD
        self.NumVerts = 0 #DWORD
    
    
    def read(self,buf):
        self.VertexIndex = buf.Read32()
        assert(self.VertexIndex == 0)
        self.ColorIndex = buf.ReadSigned32()
        assert(self.ColorIndex == -1)
        self.NumVerts = buf.Read32()
    
    
    def write(self,buf):
        buf.Write32(self.VertexIndex)
        buf.WriteSigned32(self.ColorIndex)
        buf.Write32(self.NumVerts)


class plGBufferColor:
    def __init__(self):
        self.DRed = 0
        self.DGreen = 0
        self.DBlue = 0
        self.DAlpha = 0
        self.SRed = 0
        self.SGreen = 0
        self.SBlue = 0
        self.SAlpha = 0
    
    
    def read(self,buf):
        self.DRed = buf.ReadByte()
        self.DGreen = buf.ReadByte()
        self.DBlue = buf.ReadByte()
        self.DAlpha = buf.ReadByte()
        self.SRed = buf.ReadByte()
        self.SGreen = buf.ReadByte()
        self.SBlue = buf.ReadByte()
        self.SAlpha = buf.ReadByte()
    
    
    def write(self,buf):
        buf.WriteByte(self.DRed)
        buf.WriteByte(self.DGreen)
        buf.WriteByte(self.DBlue)
        buf.WriteByte(self.DAlpha)
        buf.WriteByte(self.SRed)
        buf.WriteByte(self.SGreen)
        buf.WriteByte(self.SBlue)
        buf.WriteByte(self.SAlpha)


class plGBufferGroup:
    def __init__(self):
        self.VertStrideLength = 0 #Byte
        #...
        #self.VertexStorageLengths = hsTArray()#<ulong> - not used
        #self.VertexInfo1 = hsTArray()#<ulong> - not used
        #self.VertexInfo2 = hsTArray()#<long> - not used
        self.VertexStorage = hsTArray()#<Vertex>
        #self.ColorStorageLengths = hsTArray()#<ulong> - not used
        #self.ColorStorage = hsTArray()#<plGBufferColor> - not used
        #self.IndexStorageLengths = hsTArray()#<ulong> - redundant; use length of each IndexStorage element instead
        #self.IndexInfo1 = hsTArray()#<ulong> - not used
        #self.IndexInfo2 = hsTArray()#<long> - not used
        self.IndexStorage = hsTArray()#<Int16>
        #...
        self.PackedVertexFormat = 0 #Byte
        self.VertAndColorStrideLength = 0 #?
        self.Cells = hsTArray()#<hsTArray<plGBufferCell>>
        # Added Slice attributes
        self.blend=0
        self.NumBlendWeights=0
        self.ntex=0
    
    
    def ICalcVertexSize(self):
        self.blend = (self.PackedVertexFormat>>6) & 0x01 #vertex linked to bones
        self.NumBlendWeights = (self.PackedVertexFormat>>4) & 0x03 # Vertex deformations
        self.ntex = (self.PackedVertexFormat & 0x0F) # Number of texture coords per vertex
        strideLengthSansColor = (((self.ntex + 2) * 12) + (self.NumBlendWeights << 2))
        if(self.NumBlendWeights and self.blend):
            strideLengthSansColor += 4
        self.VertStrideLength = strideLengthSansColor
        return self.VertStrideLength + 8

    
    def read(self,buf):
        self.PackedVertexFormat = buf.ReadByte()
        self.VertAndColorStrideLength = self.ICalcVertexSize()
        # This is written out in write(), but when read in, it
        # doesn't seem to be used anywhere
        localSize = buf.Read32()
        coder = plVertexCoder()
        numVertexStorages = buf.Read32()
        assert(numVertexStorages == 1)
        for j in range(numVertexStorages):
            if(self.PackedVertexFormat & 0x80): #w00t Skinny!!!
                count = buf.Read16()
                print "reading geometry (%i vertexs (%i,%i,%i)) - this will take several minutes..." %(count,self.blend,self.NumBlendWeights,self.ntex)
                coder.read(self,buf,count)
            elif 1: 
                # RTR: Currently have no way to translate
                # non-compressed data into vertices that Blender can use
                raise RuntimeError, "Encountered non-compressed vertex data - not supported"
            else: #I'm so fat... *Sigh*
                bufferSize = buf.Read32()
                self.VertexStorageLengths.append(bufferSize)
                #self.VertexInfo1.append(0)
                #self.VertexInfo2.append(-1)
                buffer = [bufferSize]
                self.VertexStorage.append(buffer)
                buf.read(bufferSize) #Garbage?
                colorCount = buf.Read32()
                self.ColorStorageLengths.append(colorCount) #w00t actual usage!!!
                if colorCount:
                    for i in range(colorCount):
                        j = plGBufferColor()
                        j.read(buf)
                        self.ColorStorage.append(j)
        i = buf.Read32()
        for j in range(i): 
            indexStorageLength = buf.Read32()
            indexList = hsTArray()
            for k in range(indexStorageLength):
                indexList.append(buf.Read16())
            self.IndexStorage.append(indexList)
        for j in range(numVertexStorages):
            count = buf.Read32()
            assert(count == 1)
            cells = hsTArray()
            for j in range(count):
                cell = plGBufferCell()
                cell.read(buf)
                cells.append(cell)
            self.Cells.append(cells)

        
    def write(self,buf): #Compressed Output ONLY
        # Form the format bits
        self.PackedVertexFormat |= 0x80
        self.PackedVertexFormat |= self.blend<<6
        self.PackedVertexFormat |= self.NumBlendWeights<<4
        self.PackedVertexFormat |= self.ntex
        # RTR: We only write out one vertex storage - and so does Cyan!
        numVertexStorages = 1
        # Calculate the size
        size = 0
        for i in range(numVertexStorages):
            size += len(self.VertexStorage)
        for i in range(len(self.IndexStorage)):
            size += len(self.IndexStorage[i])
        buf.WriteByte(self.PackedVertexFormat)
        buf.Write32(size)
        coder = plVertexCoder()
        buf.Write32(numVertexStorages)
        for i in range(numVertexStorages):
            count = len(self.VertexStorage)
            buf.Write16(count)
            print "storing geometry (%i vertexs (%i,%i,%i)) - this will take several minutes..."  %(count,self.blend,self.NumBlendWeights,self.ntex)
            coder.write(self,buf,count)
        buf.Write32(len(self.IndexStorage))
        for i in range(len(self.IndexStorage)):
            indexStorageLength = len(self.IndexStorage[i])
            buf.Write32(indexStorageLength)
            for j in range(indexStorageLength):
                buf.Write16(self.IndexStorage.vector[i].vector[j])
        # RTR: put in one cell in a list for now
        cell = plGBufferCell()
        cell.VertexIndex = 0
        cell.ColorIndex = -1
        cell.NumVerts = len(self.VertexStorage)
        cells = hsTArray()
        cells.append(cell)
        self.Cells.append(cells)
        for i in range(numVertexStorages):
            buf.Write32(len(self.Cells.vector[i]))
            for j in range(len(self.Cells.vector[i])):
                self.Cells.vector[i].vector[j].write(buf)

    

class plDISpanIndex: #NOTE: This is a pile of guesswork
    def __init__(self):
        self.flags = 0 #DWORD; 0x00: mesh, 0x01: bone
        self.indices = hsTArray()#<Int32>
    
    
    def read(self,buf):
        self.flags = buf.Read32()
        # Sanity check
        if not self.flags in [0x00,0x01]:
            raise RuntimeError, "plDISpanIndex.flags is %i"%flag
        i = buf.Read32()
        for j in range(i):
            self.indices.append(buf.Read32())
    
    
    def write(self,buf):
        buf.Write32(self.flags)
        buf.Write32(len(self.indices))
        for index in self.indices.vector:
            buf.Write32(index)


class plSpaceTreeNode:
    def __init__(self):
        self.box = hsBounds3Ext()
        # on leaves the box matches with the mesh transformed bbox
        # on branches, the box should contain all child objects (they are also on axis)
        #self.subsets = None #plIcicle
        #self.leaf = 0
        self.flags = 0 #WORD
        # 0x01 - leave
        # 0x00 - branch
        # 0x04 flag - unknown? (balance?)
        # 0x08 strange flag - unknown data stored in p,l.r fields
        self.parent = 0 #WORD
        # -1 on root node
        self.left = 0 #WORD
        # left child node if branch, link to mesh if leave
        self.right = 0 #WORD
        # 0 if leave, else child node
    
    
    def read(self,buf):
        self.box.read(buf)
        self.flags = buf.Read16()
        if self.flags not in [0x00,0x01,0x04,0x05,0x08]:
            raise "plSpaceTreeNode has unknown flag " + self.flags
        self.parent = buf.ReadSigned16()
        self.left = buf.Read16()
        self.right = buf.Read16()
        if self.flags & 0x01:
            assert(self.right==0)
   
    
    def write(self,buf):
        self.box.write(buf)
        buf.Write16(self.flags)
        buf.WriteSigned16(self.parent)
        buf.Write16(self.left)
        buf.Write16(self.right)

    
    def dump(self):
        print str(self)
    
    
    def __str__(self):
        return "b:%s,t:%X,p:%04i,l:%04i,r:%04i" %(str(self.box),self.flags,self.parent,self.left,self.right)


class plSpaceTree:
    def __init__(self,version):
        self.version = version
        self.type = 0x8000 # Default to NULL tree
        self.totalNodesMinusRoot = 0
        self.numLeaves = 0
        self.nodes = hsTArray() #plSpaceTreeNode
    
    
    def read(self,buf,numIcicles):
        self.type = buf.Read16()
        if (self.type == 0x8000): # NULL
            return
        # Sanity check 1: type
        if self.version==6:
            assert(self.type==0x0240) #plSpaceTree
        else:
            assert(self.type==0x0258) #plSpaceTree
        self.totalNodesMinusRoot = buf.Read16()
        # Sanity Check 2: this binary space tree should have (2 * numIcicles) -1 nodes
        assert(self.totalNodesMinusRoot == ((2*numIcicles)-2) or (self.totalNodesMinusRoot == 0 and numIcicles == 0))
        self.numLeaves = buf.Read32()
        # Sanity Check 3: leaves vs icicles
        assert(self.numLeaves == numIcicles)
        numNodes = buf.Read32()
        # Sanity check 4: nodes vs total
        assert(numNodes == (self.totalNodesMinusRoot+1))
        for i in range(numNodes):
            node = plSpaceTreeNode()
            node.read(buf)
            # Sanity check 5: node flags
            if node.flags & 0x01:
                assert(node.left==i)
                assert(i<numIcicles)
                #print i, node.parent, numIcicles
                assert(node.parent>=numIcicles or (node.parent==-1 and numIcicles==1))
            if node.flags==0x00:
                assert(i>=numIcicles)
            if node.flags & 0x08:       #female,male,live_male,live_female
                if node.parent not in [0x0A52,0x290A,0x0000]:
                    raise "tree parent is %04X" % node.parent
                if node.left not in [0xE120,0x000A,0x0000]:
                    raise "tree left is %04X" % node.left
                if node.right not in [0x070F,0x676E,0x0000,0x3F80]:
                    raise "tree right is %04X" % node.right
            self.nodes.append(node)
    
    
    def write(self,buf):
        buf.Write16(self.type)
        if self.type == 0x8000: # NULL
            return
        buf.Write16(self.totalNodesMinusRoot)
        buf.Write32(self.numLeaves)
        buf.Write32(len(self.nodes))
        for node in self.nodes.vector:
            node.write(buf)


class plDrawableSpans(hsKeyedObject):    
    def __init__(self,parent,name="unnamed"):
        hsKeyedObject.__init__(self,parent,name,0x004C)
        #self.type = 0 #DWORD -- not used
        #self.deviceCreated = 0 #DWORD -- not used
        self.localBounds = hsBounds3Ext() # flags == 0x00: max and min vertex of the entire set
        self.worldBounds = hsBounds3Ext() # flags == 0x01: same
        self.otherBounds = hsBounds3Ext() # flags == 0x01: same
        #self.L2WMatrix = hsMatrix44() # not used
        #self.W2LMatrix = hsMatrix44() # not used
        # Notice!!! Do NOT ReadVector or WriteVector with Typless Classes!
        #
        # Below are sets of 4 matrices (2 pairs, normal and inverted)
        # they contain transformations indexed in the groups with flag 0x01
        # these are bones. matrices are local, bones have a coordinate
        # interface for correct placement in the age
        self.diSpansMatrix1 = hsTArray() #hsMatrix44
        self.diSpansMatrix2 = hsTArray() #hsMatrix44
        self.diSpansMatrix3 = hsTArray() #hsMatrix44
        self.diSpansMatrix4 = hsTArray() #hshsMatrix44
        self.materials = hsTArray([0x07],self.getVersion()) #hsGMaterial
        self.tree = plSpaceTree(self.getVersion())
        #self.bv1 = hsBitVector() # not used
        #self.bv2 = hsBitVector() # not used
        #self.bv3 = hsBitVector() # not used
        #self.bv4 = hsBitVector() # not used
        #self.bv5 = hsBitVector() # not used
        self.icicles = hsTArray() #plIcicle
        self.icicles2 = hsTArray() #plIcicle
        #self.icicles3 = hsTArray() #plIcicle - not used
        #self.nTrnsfrm = hsTArray() #plSpan - not used
        self.diSpans = hsTArray() #plGBufferGroup
        self.indices = hsTArray() #plDISpanIndex
        self.properties = 0 #DWORD
        self.dsNumber = 0 #DWORD
        self.dsiD = 0 #DWORD
        #self.particleSpan = hsBitVector() - not used
        #self.blendingSpan = hsBitVector() - not used
        self.scene=UruObjectRef() #link to the plSceneNode
        self.spans = hsTArray() #plGeometrySpan
    
    
    def changePageRaw(self,sid,did,stype,dtype):
        hsKeyedObject.changePageRaw(self,sid,did,stype,dtype)
        for i in self.materials:
            i.changePageRaw(sid,did,stype,dtype)
        for icicle in self.icicles:
            for light in icicle.lights1:
                light.changePageRaw(sid,did,stype,dtype)
            for light in icicle.lights2:
                light.changePageRaw(sid,did,stype,dtype)
        self.scene.changePageRaw(sid,did,stype,dtype)

    
    def read(self,buf):
        hsKeyedObject.read(self,buf)
        self.properties = buf.Read32()
        self.dsNumber = buf.Read32()
        self.dsiD = buf.Read32()
        # sanity checks
        if self.properties not in [0x00,0x04,0x08,0x0C,0x20,0x28,0x80,0x84,0x8C,0x90,0x0200,0x0204,0x020C]:
            raise RuntimeError, "Unknown self.properties %08X" %self.properties
        if self.dsiD not in [0x00,0x01,0x04,0x08,\
             0x10000004,0x10000008,0x1000000C,0x10000010,0x10000014,0x10000018,0x1000001C,0x10000020,0x10000024,0x10000028,0x1000002C,\
  0x20000000,0x20000004,0x20000008,0x2000000C,0x20000010,0x20000014,0x20000018,0x2000001C,\
  0x40000000,0x40000004,0x40000008,0x4000000C,0x40000010,0x40000014,0x40000018,0x4000001C,\
             0x0FFFFFFE]:
            raise RuntimeError, "Unknown self.dsiD %08X" %self.dsiD
        self.materials.ReadVector(buf)
        i = buf.Read32()
        for j in range(i):
            icicle = plIcicle()
            icicle.read(buf)
            self.icicles.append(icicle)
        i = buf.Read32()
        for j in range(i):
            icicle = plIcicle()
            icicle.read(buf)
            self.icicles2.append(icicle)
        count = buf.Read32()
        for i in range(count):
            index = buf.Read32()
            # This is just the same as i; it is used to set the
            # internal NativeTransform array, which is just the
            # same as the Icicles array. So, we will do nothing with it.
        for i in range(count):
            # These references are always nil
            self.icicles[i].fog.read(buf)
        if (count):
            self.localBounds.read(buf)
            self.worldBounds.read(buf)
            self.otherBounds.read(buf)
        else:
            self.localBounds.flags |= 2
            self.worldBounds.flags |= 2
            self.otherBounds.flags |= 2
        for i in range(count):
            if (self.icicles[i].flags & 0x80):
                self.icicles[i].lights1.ReadVector(buf)
            if (self.icicles[i].flags & 0x100):
                self.icicles[i].lights2.ReadVector(buf)
        i = buf.Read32()
        for j in range(i):
            # There are never any geometry spans in the Cyan ages,
            # so this code will never get exercised
            geometrySpan = plGeometrySpan()
            geometrySpan.read(buf)
            self.spans.append(geometrySpan)
        i = buf.Read32()
        for j in range(i):
            k = hsMatrix44()
            k.read(buf)
            self.diSpansMatrix1.append(k)
            l = hsMatrix44()
            l.read(buf)
            self.diSpansMatrix2.append(l)
            m = hsMatrix44()
            m.read(buf)
            self.diSpansMatrix3.append(m)
            n = hsMatrix44()
            n.read(buf)
            self.diSpansMatrix4.append(n)
        i = buf.Read32()
        for j in range(i):
            spanIndex = plDISpanIndex()
            spanIndex.read(buf)
            self.indices.append(spanIndex)
        i = buf.Read32()
        for j in range(i):
            bufferGroup = plGBufferGroup()
            bufferGroup.read(buf)
            self.diSpans.append(bufferGroup)
        self.tree.read(buf,len(self.icicles))
        self.scene.setVersion(self.getVersion())
        self.scene.read(buf)
        assert(self.scene.verify(self.Key))

    
    def write(self,buf):
        hsKeyedObject.write(self,buf)
        buf.Write32(self.properties)
        buf.Write32(self.dsNumber)
        buf.Write32(self.dsiD)
        self.materials.WriteVector(buf)
        buf.Write32(len(self.icicles))
        for icicle in self.icicles.vector:
            icicle.write(buf)
        buf.Write32(len(self.icicles2))
        for icicle in self.icicles2.vector:
            icicle.write(buf)
        count=len(self.icicles)
        buf.Write32(count)
        for i in range(count):
            buf.Write32(i)
        for i in range(count):
            self.icicles[i].fog.write(buf)
        if count!=0:
            self.localBounds.write(buf)
            self.worldBounds.write(buf)
            self.otherBounds.write(buf)
        for i in range(count):
            if (self.icicles[i].flags & 0x80):
                self.icicles[i].lights1.WriteVector(buf)
            if (self.icicles[i].flags & 0x100):
                self.icicles[i].lights2.WriteVector(buf)
        buf.Write32(len(self.spans))
        for geometrySpans in self.spans.vector:
            geometrySpans.write(buf)
        buf.Write32(len(self.diSpansMatrix1))
        for i in range(len(self.diSpansMatrix1)):
            self.diSpansMatrix1[i].write(buf)
            self.diSpansMatrix2[i].write(buf)
            self.diSpansMatrix3[i].write(buf)
            self.diSpansMatrix4[i].write(buf)
        buf.Write32(len(self.indices))
        for spanIndex in self.indices.vector:
            spanIndex.write(buf)
        buf.Write32(len(self.diSpans))
        for bufferGroup in self.diSpans.vector:
            bufferGroup.write(buf)
        self.tree.write(buf)
        self.scene.update(self.Key)
        assert(self.scene.flag == 0x01)
        self.scene.write(buf)


    def dump(self):
        print "BEGIN DrawableSpans %s " % self.Key.name
        print("properties %08X, dsNumber %08X, dsiD %08X" % (self.properties,self.dsNumber,self.dsiD))
        print "Material dump - %i" % len(self.materials)
        i=0
        for mat in self.materials:
            print "%03i" %i ,
            print mat
            i=i+1
        print "There are %i icicles" % (len(self.icicles))
        i=0
        for icicle in self.icicles:
            print "%04i" %i,
            icicle.dump()
            i=i+1
        print self.localBounds
        print self.worldBounds
        print self.otherBounds
        print "Matrices dump - total %i" %len(self.diSpansMatrix1)
        print "Groups Dump - %i" %len(self.indices)
        for i in range(len(self.indices)):
            print "[%03i] %i:" %(i,self.indices[i].flags),
            for itm in self.indices[i].indices.vector:
                print itm,
            print ""
        print "VertexDB Dump - %i" %len(self.diSpans)
        i=0
        for s in self.diSpans:
            print "%04i" %i,
            s.dump()
            i=i+1
        print "Binary Space Tree - %i(%i)" %(len(self.tree.nodes),len(self.icicles))
        i=0
        for t in self.tree:
            print "%04i" %i,
            t.dump()
            i=i+1
        print "Scene: %s" % self.scene
        print "END DrawableSpans"

    
    def updateName(self):
        if self.dsiD==0:
            sfx="Spans"
        else:
            sfx="BlendSpans"
        root=self.getRoot()
        one="%08x" % self.dsiD
        two="%x" % self.dsNumber
        testname=str(root.name) + "_District_" + str(root.page) + "_" + one + "_" + two + sfx
        self.Key.name.set(testname)

    
    def import_all(self):
        for i in range(len(self.indices)):
            self.import_object(i,"obj%02i" % i)


    def import_object(self,setidx,name):
        #do object import here
        print "Importing %s set %i into blender" % (name,setidx)
        if setidx==-1 or setidx == 0xFFFFFFFF: # ugly hack to check for unsigned representation of -x
            return []
        if self.indices[setidx].flags==1:
            return []
        #options
        setnormal = 1
        updatenormals = 1
        #end options
        resmanager=self.getResManager()
        #find and add all vertexs
        spanIndex = self.indices[setidx]
        #print set
        firstname=None
        the_object_list=[]
        for i in range(len(spanIndex.indices)):
            #create the mesh object
            rid=((int(random.random()*10000)*100+setidx)*10 + i)
            mname = name[:10] + "%06X" %(rid)
            meshname = name[:10] + "m%06X" %(rid)
            obj = NMesh.New(meshname)
            icicle = self.icicles[spanIndex.indices[i]]
            bufferGroup = self.diSpans[icicle.vdbidx]
            surface = bufferGroup.IndexStorage[icicle.surfaceIdx]
            verts=bufferGroup.VertexStorage
            #Find the material
            matref=self.materials[icicle.subMaterialType]
            themat=resmanager.findref(matref)
            textures=[]
            if themat==None:
                print "Material %s not found" %str(matref)
            else:
                bmat=themat.data.getBlenderMaterial()
                textures=themat.data.getBlenderTextures()
                obj.addMaterial(bmat)
                #for texi in range(len(textures)):
            for e in range(icicle.vertexCount):
                s=icicle.vertexStart + e
                vert = NMesh.Vert(verts[s].X(),verts[s].Y(),verts[s].Z())
                #set vertex normal
                if setnormal==1:
                    vert.no[0]=verts[s].nx
                    vert.no[1]=verts[s].ny
                    vert.no[2]=verts[s].nz
                obj.verts.append(vert)
                #print "Adding vertex %i %i %s" % (e,s,verts[s])
            #vertices done, now add faces
            e=0
            print "Number of textures %i - %i" %(len(textures),bufferGroup.ntex)
            ### I ALREADY KNOW THAT THIS CODE IS WRONG, YES IS WRONG VERY WRONG!!!!!!
            ### THIS IS WRONG, I REPEAT IS WRONG, WORKS BY PURE CASUALITY
            ### I WILL FIX IT WHEN I FIND SOME TIME TO DO IT  
            while e < icicle.triCount:
                s=icicle.idxStart + e
                face = NMesh.Face()
                f_uv=[]
                for w in range(3):
                    pi=surface[s+w] #vertex in surface(faces table)
                    r=(surface[s+w])-icicle.vertexStart
                    face.append(obj.verts[r])
                    vert=verts[pi]
                    col = NMesh.Col(verts[pi].color.r,verts[pi].color.g,verts[pi].color.b,verts[pi].color.a)
                    face.col.append(col)
                    #print r
                    #assert(len(textures)==len(vert.tex))
                    f_uv.append(vert.tex)
                texidx=0
                c=[]
                print len(f_uv[0]), bufferGroup.ntex, len(textures)
                assert(len(f_uv[0])==bufferGroup.ntex)
                if bufferGroup.ntex!=0:
                    for i in range(len(textures)):
                        if i>=bufferGroup.ntex:
                            texi=bufferGroup.ntex-1
                        else:
                            texi=i
                        c=[]
                        for uvco in f_uv:
                            c.append(uvco[texi])
                        if (c[0][0]==c[1][0] and c[0][1]==c[1][1]) or \
                           (c[0][0]==c[2][0] and c[0][1]==c[2][1]) or \
                           (c[1][0]==c[2][0] and c[1][1]==c[2][1]):
                            continue
                        else:
                            texidx=texi
                            break
                if len(textures)!=0 and bufferGroup.ntex!=0:
                    if textures[texidx]!=None:
                        face.image=textures[texidx].data.getBlenderImage()
                        xixu=[(c[0][0],1-c[0][1]),(c[1][0],1-c[1][1]),(c[2][0],1-c[2][1])]
                        #print xixu
                        face.uv=xixu

                face.smooth=1
                face.materialIndex = 0
                #print "added face"
                obj.faces.append(face)
                e=e+3
            #--
            #obj.setMaxSmoothAngle(30)
            matrix=icicle.L2WMatrix.get()
            matrix.transpose()
            #####obj.transform(matrix,0)
            obj.hasVertexColours(1)
            if len(textures)!=0:
                obj.hasFaceUV(1)
            sobj=NMesh.PutRaw(obj,mname,updatenormals)
            sobj.setName(mname)
            #set object position (transmatrix)
            sobj.setMatrix(matrix)
            ##props
            sobj.addProperty("name",name)
            sobj.addProperty("page_num",str(self.getPageNum()))
            #material
            sobj.addProperty("material",str(matref.Key.name))
            if themat==None:
                print "Material %s not found" %str(matref)
            else:
                bmat=themat.data.getBlenderMaterial()
                sobj.setMaterials([bmat,])
                sobj.colbits=0x01
            #Lighting
            a=struct.pack(">I",icicle.flags)
            b=binascii.hexlify(a).upper()
            sobj.addProperty("light",b)
            lint=0
            for a in icicle.lights1.vector+icicle.lights2.vector:
                cstrl="lights%03i" %lint
                sobj.addProperty(cstrl,str(a.Key.name))
                lint=lint+1
            sobj.layers=[1,]
            ##
            #sobj.data.update()
            the_object_list.append(sobj)
        return the_object_list


    def find_buffer_group(self,blend,nblends,ntex,num_vertexs):
        #blend - informaci&oacute;n &oacute;sea
        #nblends - vertexs groups
        #ntex - number of sets of 3 texcoords
        for i in range(len(self.diSpans)):
            bufferGroup=self.diSpans[i]
            if bufferGroup.blend==blend and bufferGroup.NumBlendWeights==nblends and bufferGroup.ntex==ntex \
                        and len(bufferGroup.VertexStorage)+num_vertexs<0x8000:
                return i
        #not found - create a new bufferGroup
        bufferGroup=plGBufferGroup()
        bufferGroup.blend=blend
        bufferGroup.NumBlendWeights=nblends
        bufferGroup.ntex=ntex
        self.diSpans.append(bufferGroup)
        return len(self.diSpans)-1


    def findMaterial(self,name):
        for i in range(len(self.materials)):
            if str(self.materials[i].Key.name)==name:
                return i
        #not found
        root=self.getRoot()
        mat=root.find(0x07,name,0)
        if mat==None:
            print "WARNING: I cannot find material %s" %name
            return None
        self.materials.append(mat.data.getRef())
        return len(self.materials)-1


    def createMaterial(self,mat=None,name=None,texture=None):
        # creates basic (old-style) material if though some fluke it isn't there yet.
        if name==None:
            if mat==None:
                name="alcNullMaterial"
            else:
                name=mat.name
        root=self.getRoot()
        pmat=root.find(0x07,name,1)
        pmat.data.addTextureLayer(texture,mat)
        pmat.data.ensureBasicMaterial(mat)
        return self.findMaterial(name)


    def export_object(self,obj,dynamic=0,hasTextures=0):
        # object analisis
        name = obj.name
        meshname = obj.data.name
        mesh = Blender.NMesh.GetRaw(meshname)
        faces=[]
        vertexs=mesh.verts
        print "exporting %s %i vertexs, please wait several minutes" %(name,len(vertexs))
        hastextureface=0
        ntextures=0
        texture_img=None
        for f in mesh.faces:
            if f.image!=None:
                hastextureface=1
                texture_img=f.image
        if hastextureface or hasTextures:
            ntextures=1

        #determine the correct buffer group to store all
        sliceid=self.find_buffer_group(0,0,ntextures,len(vertexs))
        bufferGroup=self.diSpans[sliceid]
        vstart=len(bufferGroup.VertexStorage)
        vstop=vstart + len(vertexs) #wrong!!! - don't trust it
        ##the matrix
        the_matrix=getMatrix(obj)
        the_matrix.transpose()
        matrix=hsMatrix44()
        matrix.set(the_matrix)
        #THE NEW CODE
        v_idx=0
        for face in mesh.faces:
            n=len(face.v)
            if n<3:
                continue
            elif n==3:
                for i in range(3):
                    vertex=face.v[i]
                    vert=Vertex(vertex.co[0],vertex.co[1],vertex.co[2])
                    if not dynamic:
                        vert.transform(matrix)
                    #set some properties
                    vert.nx = vertex.no[0]
                    vert.ny = vertex.no[1]
                    vert.nz = vertex.no[2]
                    vert.tex = [[0,0,0],]
                    if face.image!=None or hasTextures:
                        try:
                            vert.tex = [[face.uv[i][0],1-face.uv[i][1],0],]
                        except IndexError:
                            pass
    
                    try:
                        vert.color.set(face.col[i].r,face.col[i].g,face.col[i].b,face.col[i].a)
                    except IndexError:
                        pass
                    bufferGroup.VertexStorage.append(vert)
                    faces.append(v_idx)
                    v_idx=v_idx+1
            elif n==4:
                for i in (0,1,2,0,2,3):
                    vertex=face.v[i]
                    vert=Vertex(vertex.co[0],vertex.co[1],vertex.co[2])
                    if not dynamic:
                        vert.transform(matrix)
                    #set some properties
                    vert.nx = vertex.no[0]
                    vert.ny = vertex.no[1]
                    vert.nz = vertex.no[2]
                    vert.tex = [[0,0,0],]

                    if face.image!=None or hasTextures:
                        try:
                            vert.tex = [[face.uv[i][0],1-face.uv[i][1],0],]
                        except IndexError:
                            pass

                    try:
                        vert.color.set(face.col[i].r,face.col[i].g,face.col[i].b,face.col[i].a)
                    except IndexError:
                        pass
                    bufferGroup.VertexStorage.append(vert)
                    faces.append(v_idx)
                    v_idx=v_idx+1
            else:
                raise RuntimeError, "this mesh has faces with %i vertexs, please reduce the number of vertexs per face to 3 or 4" %n
            #print face.col
        #That was the easy part, now the extremely hard one
        vstop = vstart + v_idx
        #NEW CODE ENDS
        #set the surfaceid
        surfaceid=0 #32 bits
        if len(bufferGroup.IndexStorage)==0:
            face=hsTArray()
            bufferGroup.IndexStorage.append(face)
        idxstart=len(bufferGroup.IndexStorage[surfaceid])
        idxstop=idxstart + len(faces)
        for face in faces:
            bufferGroup.IndexStorage[surfaceid].append(face + vstart)
        #find the material
        try:
            p=obj.getProperty("material")
            name=str(p.getData())
            matidx=self.findMaterial(name)
        except AttributeError:
            matidx=None
        if matidx==None:
            if len(obj.data.materials)>0:
                matidx=self.findMaterial(obj.data.materials[0].name)
                if matidx==None:
                    matidx=self.createMaterial(obj.data.materials[0],texture=texture_img)
            else:
                matidx=self.findMaterial("prpNullMaterial")
                if matidx==None:
                    matidx=self.createMaterial(None,"prpcNullMaterial")
        #Now create a new icicle
        icicle = plIcicle()
        icicle.subDrawableType=1
        icicle.subMaterialType=matidx
        # set lighting flags
        try:
            p=obj.getProperty("light")
            name=str(p.getData())
            while len(name)<8:
                name = "0" + name
            b=binascii.unhexlify(str(name))
            l,=struct.unpack(">I",b)
        except AttributeError:
            l=0
        icicle.flags=l
	# Obtain the list of lights that affect this object.
    # Try to get properties lights000 through lights999.
    # Realistically, the user would only specify a few lights at
	# most, and so the "for" loop below will not go all the way to
	# 1000.  The artists cannot blame the plugin for limiting their
	# creativity!
        for lint in range(1000):
            cstrl="lights%03i" %lint
            lightName=None
            try:
                p=obj.getProperty(cstrl)
                lightName=str(p.getData())
            except AttributeError:
                break
            if lightName != None:
                # First, look for a directional light with that name
                resmanager=self.getRoot() #ResManager()
                theLightObj=resmanager.find(0x55,lightName,0)
                if theLightObj==None:
                    # Next, look for a omni light with that name
                    theLightObj=resmanager.find(0x56,lightName,0)
                    if theLightObj==None:
                        # Finally, look for a spot light
                        theLightObj=resmanager.find(0x57,lightName,0)
                        if theLightObj==None:
                            raise RuntimeError, "Could not locate light %s as listed by object %s" %(lightName,obj.name)

                theLight = theLightObj.data.getRef()
                # Add the light to the mesh's first light array
                # Note: the 0x0080 flag in the mesh corresponds to 
                # any light type, not just directional and omni.
                icicle.lights1.append(theLight)

        icicle.vdbidx=sliceid #* Slice to look up for vertex arrays
        icicle.vertexStart=vstart #* First vertex offset
        icicle.vertexStart2=vstart
        icicle.vertexCount=vstop-vstart #* Number of vertexs of this mesh
        icicle.surfaceIdx=surfaceid #* Inside that slice, surface to look up for faces
        icicle.idxStart=idxstart #* First vertex index of the first face
        icicle.triCount=idxstop-idxstart #* Last vertex of the last face
        # set transformation info
        if dynamic==1:
            matrix=getMatrix(obj)
            matrix.transpose()
            icicle.L2WMatrix.set(matrix)
            matrix=getMatrix(obj)
            matrix.invert()
            matrix.transpose()
            icicle.W2LMatrix.set(matrix)
        else: #information already transformed on non-dynamic objects
            icicle.L2WMatrix.identity()
            icicle.W2LMatrix.identity()
        #to compute an off-axis bbox
        # 1) Find the vertex nearest to the min of the on-axis bbox
        # 2) Find the 3 vertex's with are nearest to the min vertex
        # 3) Set the corner as the min vertex, and the 3 coords as (vi-min_vertex)
        #-- I will get a transformed global bbox in this way, we need to multiply it by the
        # inverse matrix to get the local bbox.
        # set the bboxes - these are on-axis
        bvertexs=obj.getBoundBox()
##        maxx=0
##        maxy=0
##        maxz=0
##        minx=vertexs[0].co[0]
##        miny=vertexs[0].co[1]
##        minz=vertexs[0].co[2]
##
##        for vertex in vertexs:
##            if vertex.co[0]>maxx:
##                maxx=vertex.co[0]
##            if vertex.co[0]<minx:
##                minx=vertex.co[0]
##            if vertex.co[1]>maxy:
##                maxy=vertex.co[1]
##            if vertex.co[1]<miny:
##                miny=vertex.co[1]
##            if vertex.co[2]>maxz:
##                maxz=vertex.co[2]
##            if vertex.co[2]<minz:
##                minz=vertex.co[2]
        bmaxx=0
        bmaxy=0
        bmaxz=0
        bminx=bvertexs[0][0]
        bminy=bvertexs[0][1]
        bminz=bvertexs[0][2]
        for vertex in bvertexs:
            if vertex[0]>bmaxx:
                bmaxx=vertex[0]
            if vertex[0]<bminx:
                bminx=vertex[0]
            if vertex[1]>bmaxy:
                bmaxy=vertex[1]
            if vertex[1]<bminy:
                bminy=vertex[1]
            if vertex[2]>bmaxz:
                bmaxz=vertex[2]
            if vertex[2]<bminz:
                bminz=vertex[2]
        #print maxx,maxy,maxz,minx,miny,minz
        print bmaxx,bmaxy,bmaxz,bminx,bminy,bminz
        ##hull=alcConvexHull(bvertexs)
        ##bvertexs=hull.vertexs
        #compute the off-axis bbox
##        dmin=distance(bvertexs[0],[bminx,bminy,bminz])
##        imin=0
##        for i in range(len(bvertexs)):
##            vertex=bvertexs[i]
##            d=distance(vertex,[bminx,bminy,bminz])
##            if d<dmin:
##                dmin=d
##                imin=i
        #the corner (world coordinates)
        vmin=Vertex(bminx,bminy,bminz)
        lvmin=Vertex(vmin.x,vmin.y,vmin.z)
        lvmin.transform(icicle.W2LMatrix)
        vmax=Vertex(bmaxx,bmaxy,bmaxz)
        lvmax=Vertex(vmax.x,vmax.y,vmax.z)
        lvmax.transform(icicle.W2LMatrix)
        ##vcorner=Vertex(bvertexs[imin][0],bvertexs[imin][1],bvertexs[imin][2])
        ##lvcorner=Vertex(vcorner.x,vcorner.y,vcorner.z)
        ##lvcorner.transform(icicle.W2LMatrix)
##        the_lyst=[]
##        for face in hull.faces:
##            if imin in face:
##                for uiy in face:
##                    if uiy==imin:
##                        continue
##                    if uiy not in the_lyst:
##                        the_lyst.append(uiy)

##        voffsets=[]
##        lvoffsets=[]
##        for f in the_lyst:
##            v=bvertexs[f]
##            vi=Vertex(v[0],v[1],v[2])
##            lvi=Vertex(v[0],v[1],v[2])
##            lvi.transform(icicle.W2LMatrix)
##            fvi=Vertex(vi.x-vcorner.x,vi.y-vcorner.y,vi.z-vcorner.z)
##            lfvi=Vertex(lvi.x-lvcorner.x,lvi.y-lvcorner.y,lvi.z-lvcorner.z)
##            voffsets.append(fvi)
##            lvoffsets.append(lfvi)
        #local
        icicle.bounds1.min=lvmin
        icicle.bounds1.max=lvmax
        #icicle.bounds1.corner=lvcorner
        #icicle.bounds1.diff=lvoffsets
        icicle.bounds1.flags=0x01
        #world
        icicle.bounds2.min=vmin
        icicle.bounds2.max=vmax
        #icicle.boundss.corner=vcorner
        #icicle.boundss.diff=voffsets
        icicle.bounds2.flags=0x01
        self.icicles.append(icicle)
        #Add it to an existent group, for example, the number 1, that is the floor
        spanIndex = plDISpanIndex()
        spanIndex.indices.append(len(self.icicles)-1)
        self.indices.append(spanIndex)
        return len(self.indices)-1
        ####
##        self.blendflag=0 #bones count - 0 or 2-255 (cannot be 1, the first bone is always identity)
##                            # if no bone information is provided, lenght is always 2, and then we
##                            # suppose that all vertexs are linked to the 2nd bone.
##        self.blendindex=0 #bones start
##        self.unk1a=0 #possible water effect
##        self.unk1b=0 #flags? (bones)
##        self.unk2=0 #flags? (bones)
##        self.f1=-1
##        self.f2=-1
##        self.f3=0 #only if light & 0x0010
##
##        self.unk3=0
##        self.unk4=0
        ####
        """
            Compression NOTE:
            1) Locate the vertex with the lowest x,y,z coords, and set it as the first vertex of the list.
            2) Sort the list by distance to this vertex.
            3) Update the face list
            Then the compressed vertex list will be smaller.
        """

