Skip to content

Instantly share code, notes, and snippets.

@Jellonator
Last active July 6, 2018 03:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jellonator/ef48f7a1c95c5145253e04ef64626779 to your computer and use it in GitHub Desktop.
Save Jellonator/ef48f7a1c95c5145253e04ef64626779 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
import argparse
import struct
import math
import operator
parser = argparse.ArgumentParser()
parser.add_argument('input', metavar="INPUT", type=argparse.FileType('rb'),
nargs=1, help='the TMESH file to open')
parser.add_argument('output', metavar="OUTPUT", type=argparse.FileType('w'),
nargs=1, help='the OBJ file to save to')
# parser.add_argument('--verbose', action="store_const", const=True, default=False)
args = parser.parse_args()
print(args)
mesh = args.input[0]
obj = args.output[0]
# Not sure what the header is
mesh.read(96)
# Not sure what this is
something = struct.unpack(">H", mesh.read(2))[0]
# This value is used to calculate padding later
padding_mult = struct.unpack(">H", mesh.read(2))[0]
# Vertex block format:
# struct Vertex { float x, y, z; };
# uint32_t num_vertices
# Vertex vertices[num_vertices]
num_vertices = struct.unpack(">I", mesh.read(4))[0]
vertices = []
print(num_vertices, "vertices")
for i in range(num_vertices):
pos = struct.unpack(">fff", mesh.read(12))
vertices.append(pos)
obj.write("v {} {} {}\n".format(pos[0], pos[1], pos[2]))
# Texture coordinate block format:
# struct TexCoord { float x, y; };
# uint32_t num_texcoords;
# TexCoord texcoords[num_texcoords];
#
# The y-axis is reversed
num_texcoords = struct.unpack(">I", mesh.read(4))[0]
print(num_texcoords, "texcoords")
for i in range(num_texcoords):
pos = struct.unpack(">ff", mesh.read(8))
obj.write("vt {} {}\n".format(pos[0], 1.0-pos[1]))
# Normal block format:
# struct Normal { float x, y, z; };
# uint32_t num_normals
# Normal normals[num_normals]
num_normals = struct.unpack(">I", mesh.read(4))[0]
print(num_normals, "normals")
normals = []
for i in range(num_normals):
pos = struct.unpack(">fff", mesh.read(12))
# Make sure normals are unit vectors
# Not really necessary, but just in case
length = math.sqrt(pos[0]**2 + pos[1]**2 + pos[2]**2)
pos = (pos[0] / length, pos[1] / length, pos[2] / length)
normals.append(pos)
obj.write("vn {} {} {}\n".format(pos[0], pos[1], pos[2]))
# Faces are built in triangle strips
# For example, a strip with the data [0, 1, 3, 5, 8] would build the following
# triangles: [(0, 1, 3), (1, 3, 5), (3, 5, 8)]
#
# Strip vertex block format:
# struct Strip {
# uint32_t num_elements;
# uint16_t vertex_id[num_elements];
# uint32_t unknown_1;
# uint32_t tri_order; // The order that triangles will be build in (1 or 2)
# };
# uint32_t num_strips;
# Strip strips[num_strips];
strips = []
sum = 0
num_strips = struct.unpack(">I", mesh.read(4))[0]
for i in range(num_strips):
num_elements = struct.unpack(">I", mesh.read(4))[0]
elements = []
for j in range(num_elements):
elements.append(struct.unpack(">H", mesh.read(2))[0])
unknown = struct.unpack(">II", mesh.read(8))
strip = {
"elements": elements,
"unknown": unknown[0],
"triorder": unknown[1],
}
print(unknown, len(elements))
strips.append(strip)
# There's some padding here for some reason. Don't ask why the format
# calculates the padding this way, I don't know either.
mesh.read(padding_mult*num_strips)
# Here is the strip data that contains texture coordinate and
# normal information, not sure why it's separate from the vertex ids.
# Strip data format:
# struct ElementData {
# uint16_t texcoord_id;
# uint16_t normal_id;
# };
# struct StripData {
# uint32_t num_elements; // always equal to the
# // corresponding Strip.num_elements
# ElementData elements[num_elements];
# };
# uint32_t num_stripdata; // Always equal to num_strips
# StripData stripdata[num_stripdata];
stripdata = []
num_stripdata = struct.unpack(">I", mesh.read(4))[0]
for i in range(num_stripdata):
num_elements = struct.unpack(">I", mesh.read(4))[0]
elements = []
for j in range(num_elements):
elements.append(struct.unpack(">HH", mesh.read(4)))
stripdata.append(elements)
# Build triangle faces
for (strip, data) in zip(strips, stripdata):
elements = strip["elements"]
a = strip["triorder"]
b = 3-a
lists = [[0,b,a], [0,a,b]]
index = 0
for i in range(len(elements)-2):
obj.write("f ")
for j in lists[index]:
subdata = data[i+j]
texcoord = subdata[0]+1
normal = subdata[1]+1
vertex = elements[i+j]+1
if vertex > num_vertices:
print("INVALID VERTEX INDEX", vertex)
if texcoord > num_texcoords:
print("INVALID TEXCOORD INDEX", texcoord)
if normal > num_normals:
print("INVALID NORMAL INDEX", normal)
obj.write("{}/{}/{} ".format(vertex, texcoord, normal))
index = (index+1) % len(lists)
obj.write("\n")
# There's also some weird data after this point, but I'm not sure what it does
# or if it's even meaningful in the first place
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment