home *** CD-ROM | disk | FTP | other *** search
/ Xentax forum attachments archive / xentax.7z / 14707 / Spellforce050818_2.7z / msb.py < prev    next >
Encoding:
Python Source  |  2018-08-03  |  3.8 KB  |  124 lines

  1. """
  2.  
  3. Binary mesh file.
  4.  
  5. Possibly relevant strings from the executable:
  6. REB_Mesh VEC_Mesh VEC_Material VEC_Map
  7. Bitmap, UserData, Map_Diffuse, OneSided, DepthBuffered
  8. unknown render mode
  9. (various strings, possibly flags, 9088d0)
  10. Map_Bump
  11. invalid tag for a userdata (%d)
  12.  
  13. """
  14.  
  15. import io
  16. import struct
  17.  
  18. from PyQt5.QtGui import QVector2D, QVector3D
  19.  
  20. import utils
  21.  
  22.  
  23. def read_vector2d(ins):
  24.     x, y = struct.unpack('<2f', ins.read(8))
  25.     return QVector2D(x, y)
  26.  
  27.  
  28. def read_vector3d(ins):
  29.     x, y, z = struct.unpack('<3f', ins.read(12))
  30.     return QVector3D(x, y, z)
  31.  
  32.  
  33. class Vertex:
  34.     RAW_SIZE = 40
  35.     POS_OFFSET = 0
  36.     TEXCOORD_OFFSET = 28
  37.  
  38.     def __init__(self, ins):
  39.         self.pos = read_vector3d(ins)
  40.         self.normal = read_vector3d(ins)
  41.         # Perhaps ARGB, only ffffffff observed
  42.         self.u0, = struct.unpack('<L', ins.read(4))
  43.         self.texcoord = read_vector2d(ins)
  44.         self.bone, = struct.unpack('<L', ins.read(4))
  45.  
  46.  
  47. class Triangle:
  48.     RAW_SIZE = 8
  49.  
  50.     def __init__(self, ins):
  51.         # d and e unknown, seem to be very low values. Full sample needed.
  52.         self.a, self.b, self.c, self.d, self.e = \
  53.             struct.unpack('<3H2B', ins.read(8))
  54.  
  55.  
  56. class BoundingBox:
  57.     def __init__(self, ins):
  58.         minx, miny, minz, maxx, maxy, maxz = struct.unpack('<6f', ins.read(24))
  59.         self.min = QVector3D(minx, miny, minz)
  60.         self.max = QVector3D(maxx, maxy, maxz)
  61.  
  62.     def center(self):
  63.         return QVector3D((self.min.x() + self.max.x()) / 2,
  64.                          (self.min.y() + self.max.y()) / 2,
  65.                          (self.min.z() + self.max.z()) / 2)
  66.  
  67.     def radius(self):
  68.         return (self.center() - self.min).length()
  69.  
  70.  
  71. class Material:
  72.     def __init__(self, ins):
  73.         # a is always -1
  74.         # b and c are interesting, see Mesh.u2
  75.         # it looks like b is UserData (needs more testing)
  76.         # c could be flags like MappingType = "Explicit"
  77.         self.a, self.b, self.c, self.TilingU = struct.unpack('<l2Lf', ins.read(16))
  78.         # See sf1.pak, for Order of Dawn at least.
  79.         name = ins.read(64)
  80.         self.name = utils.read_cstr(name)
  81.  
  82.  
  83. def process_verts(raw_verts):
  84.     ins = io.BytesIO(raw_verts)
  85.     verts = []
  86.     while ins.tell() < len(raw_verts):
  87.         verts.append(Vertex(ins))
  88.     return verts
  89.  
  90.  
  91. class Mesh:
  92.     def __init__(self, ins):
  93.         always512, num_verts, num_tris = struct.unpack('<h2L', ins.read(10))
  94.         self.raw_verts = ins.read(Vertex.RAW_SIZE * num_verts)
  95.         self.verts = process_verts(self.raw_verts)
  96.         self.triangles = [Triangle(ins) for _ in range(num_tris)]
  97.         # Process into raw indices, which involves removing the d e fields:
  98.         self.raw_indices = bytearray()
  99.         tri_ins = io.BytesIO(self.raw_indices)
  100.         for tri in self.triangles:
  101.             # Note winding order:
  102.             self.raw_indices.extend(struct.pack('<3H', tri.c, tri.b, tri.a))
  103.         always512, = struct.unpack('<h', ins.read(2))
  104.         # Suggested names from here on are based on .msh content:
  105.         self.diffuse_material = Material(ins)
  106.         self.bump_material = Material(ins)
  107.         # u0 shine? always 0.0
  108.         self.argb0, self.argb1, self.u0 = struct.unpack('<2Lf', ins.read(12))
  109.         self.bb = BoundingBox(ins)
  110.         # u1 always 1.0 - could be opacity
  111.         # A large range for u2. Sometimes, clearly floats. Other times, they
  112.         # look like low sequential indices. Other times still, they look like
  113.         # the b and c values seen in the Maps. UserData?
  114.         self.u1, self.u2 = struct.unpack('<fL', ins.read(8))
  115.  
  116.  
  117. class MeshBuffer:
  118.     def __init__(self, data):
  119.         ins = io.BytesIO(data)
  120.         always512, num_meshes = struct.unpack('<HL', ins.read(6))
  121.         self.meshes = [Mesh(ins) for _ in range(num_meshes)]
  122.         self.bb = BoundingBox(ins)
  123.  
  124.