import ctypes import os, sys sys.path.append( os.path.abspath( os.path.join( os.path.dirname(__file__), "../../"))) from lib3ddevil1.bindings import libc del os, sys #--------------------------------------+ # Basic Struct #--------------------------------------+ class Header(ctypes.Structure): _pack_ = 1 _fields_ = [ ("numMesh", ctypes.c_ubyte), ("unknownNumberB", ctypes.c_ubyte), ("unknownNumberC", ctypes.c_ubyte), ("unknownNumberD", ctypes.c_ubyte), ("padding", ctypes.c_int), ("unknownOffset", ctypes.c_ulonglong) ] class MeshHeader(ctypes.Structure): _pack_ = 1 _fields_ = [ ("numBatch", ctypes.c_short), ("numVertex", ctypes.c_short), ("u", ctypes.c_uint), ("offsetBatches", ctypes.c_ulonglong), ("flags", ctypes.c_ulonglong) ] class Coordinate(ctypes.Structure): _pack_ = 1 _fields_ = [ ("x", ctypes.c_float), ("y", ctypes.c_float), ("z", ctypes.c_float) ] def __str__(self): return "(%s, %s, %s)" % (str(self.x), str(self.y), str(self.z)) class UVs(ctypes.Structure): _pack_ = 1 _fields_ = [ ("u", ctypes.c_short), ("v", ctypes.c_short) ] def __str__(self): return "(%s, %s)" % (str(self.u), str(self.v)) class BoneIndexes(ctypes.Structure): _pack_ = 1 _fields_ = [ ("indexes", ctypes.c_ubyte * 4), ] class BoneWeights(ctypes.Structure): _pack_ = 1 _fields_ = [ ("weights", ctypes.c_short) ] class BatchData(ctypes.Structure): _pack_ = 1 _fields_ = [ ("numVertex", ctypes.c_short), ("uB", ctypes.c_short), ("padding", ctypes.c_uint), ("offsetPositions", ctypes.c_ulonglong), ("offsetNormals", ctypes.c_ulonglong), ("offsetUVs", ctypes.c_ulonglong), ("offsetBoneIndexes", ctypes.c_ulonglong), ("offsetBoneWeights", ctypes.c_ulonglong), ("offsets", ctypes.c_ulonglong) ] class VertexData(ctypes.Structure): _pack_ = 1 _fields_ = [ ("positions", ctypes.POINTER(Coordinate)), ("normals", ctypes.POINTER(Coordinate)), ("u", ctypes.POINTER(UVs)), ("bi", ctypes.POINTER(BoneIndexes)), ("bw", ctypes.POINTER(BoneWeights)) ] class Batch(ctypes.Structure): _pack_ = 1 _fields_ = [ ("bd", ctypes.POINTER(BatchData)), ("vd", VertexData) ] class Mesh(ctypes.Structure): _pack_ = 1 _fields_ = [ ("b", ctypes.POINTER(Batch)) ] class Devil1GEO_FN(ctypes.Structure): _fields_ = [ ("printheader", ctypes.CFUNCTYPE( None, ctypes.POINTER(Header))), ("printmeshheader", ctypes.CFUNCTYPE( None, ctypes.POINTER(MeshHeader))), ("printbatch", ctypes.CFUNCTYPE( None, ctypes.POINTER(Batch))), ("printcoordinate", ctypes.CFUNCTYPE( None, ctypes.POINTER(Coordinate))), ("getheader", ctypes.CFUNCTYPE( ctypes.c_bool, ctypes.POINTER(ctypes.POINTER(Header)), ctypes.c_char_p)), ("getmeshheader", ctypes.CFUNCTYPE( ctypes.c_bool, ctypes.POINTER(ctypes.POINTER(MeshHeader)), ctypes.c_uint, ctypes.c_char_p)), ("getbatch", ctypes.CFUNCTYPE( ctypes.c_bool, ctypes.POINTER(Batch), ctypes.c_uint, ctypes.c_char_p)), ("getmesh", ctypes.CFUNCTYPE( ctypes.c_bool, ctypes.POINTER(Mesh), ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint)) ] devil1geo = Devil1GEO_FN.in_dll(libc, "DEVIL1GEO") del libc #--------------------------------------+ # Pythonic Object #--------------------------------------+ class pyGeoHeader: def __init__(self, filedata): self.cstruct = ctypes.pointer(Header()) ptrofptr = ctypes.byref(self.cstruct) if filedata: if not devil1geo.getheader(ptrofptr, filedata): raise RuntimeError("failed to get geometry file header") return def show(self): devil1geo.printheader(self.cstruct) def getnummesh(self): return self.cstruct.contents.numMesh def getunknownb(self): return self.cstruct.contents.unknownNumberB def getunknownc(self): return self.cstruct.contents.unknownNumberC def getunknownd(self): return self.cstruct.contents.unknownNumberD def getpadding(self): return hex(self.cstruct.contents.padding) def getunknownoffset(self): return hex(self.cstruct.contents.unknownOffset) class pyMeshHeader: def __init__(self, i, filedata): self.cstruct = ctypes.pointer(MeshHeader()) ptrofptr = ctypes.byref(self.cstruct) if filedata: if not devil1geo.getmeshheader(ptrofptr, i, filedata): raise RuntimeError("failed to get mesh header #" + str(i)) return pass def show(self): devil1geo.printmeshheader(self.cstruct) def getnumbatch(self): return self.cstruct.contents.numBatch def getnumvertex(self): return self.cstruct.contents.numVertex def getunknown(self): return hex(self.cstruct.contents.u) def getoffsetbatches(self): return self.cstruct.contents.offsetBatches def getflags(self): return self.cstruct.contents.flags class pyMesh: def __init__(self, i, filedata): self.cstruct = Mesh() if filedata: mh = pyMeshHeader(i, filedata) # allocate memory for the size of batch * number of batches memsize = ctypes.sizeof(Batch) * mh.getnumbatch() self.cstruct.b = ctypes.cast( ctypes.create_string_buffer(memsize), ctypes.POINTER(Batch)) if not devil1geo.getmesh( ctypes.byref(self.cstruct), i, filedata, len(filedata)): raise RuntimeError("failed to get mesh") del mh, memsize return def show(self): if self.cstruct.b: devil1geo.printbatch(self.cstruct.b) else: print("nothing to show") def getbatchdata(self): return self.cstruct.b.contents.bd.contents def getpositions(self): length = self.cstruct.b.contents.bd.contents.numVertex return self.cstruct.b.contents.vd.positions[:length] def getnormals(self): length = self.cstruct.b.contents.bd.contents.numVertex return self.cstruct.b.contents.vd.normals[:length] def getuvs(self): length = self.cstruct.b.contents.bd.contents.numVertex return self.cstruct.b.contents.vd.u[:length] def getboneindexes(self): length = self.cstruct.b.contents.bd.contents.numVertex return self.cstruct.b.contents.vd.bi[:length] def getboneweights(self): length = self.cstruct.b.contents.bd.contents.numVertex return self.cstruct.b.contents.vd.bw[:length]