280 lines
12 KiB
Python
Executable File
280 lines
12 KiB
Python
Executable File
#!/bin/env python3
|
|
|
|
import argparse
|
|
import struct
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("file")
|
|
parser.add_argument("name")
|
|
parser.add_argument('mode', help='obj parsing mode', nargs='?', choices=('line', 'face', 'facen', 'obj', 'collision'), default='line')
|
|
parser.add_argument('-c', '--color', help='enable color (lines)', action='store_true')
|
|
parser.add_argument('-b', '--binary', help='binary output', action='store_true')
|
|
parser.add_argument('-d', '--debug', help='print extra information', action='store_true')
|
|
args = parser.parse_args()
|
|
|
|
objFile = open(args.file)
|
|
obj = objFile.read()
|
|
objFile.close()
|
|
|
|
# Constants
|
|
BLACK = 0
|
|
BLUE = 1
|
|
GREEN = 2
|
|
CYAN = 3
|
|
RED = 4
|
|
PURPLE = 5
|
|
BROWN = 6
|
|
LTGRAY = 7
|
|
DKGRAY = 8
|
|
LTBLUE = 9
|
|
LTGREEN = 10
|
|
LTCYAN = 11
|
|
LTRED = 12
|
|
LTPURPLE = 13
|
|
YELLOW = 14
|
|
WHITE = 15
|
|
|
|
COLOR_LOOKUP = {
|
|
'BLACK': 0,
|
|
'BLUE': 1,
|
|
'GREEN': 2,
|
|
'CYAN': 3,
|
|
'RED': 4,
|
|
'PURPLE': 5,
|
|
'BROWN': 6,
|
|
'LTGRAY': 7,
|
|
'DKGRAY': 8,
|
|
'LTBLUE': 9,
|
|
'LTGREEN': 10,
|
|
'LTCYAN': 11,
|
|
'LTRED': 12,
|
|
'LTPURPLE': 13,
|
|
'YELLOW': 14,
|
|
'WHITE': 15,
|
|
}
|
|
|
|
# Variables
|
|
activeColor = 'WHITE'
|
|
verts = []
|
|
lines = []
|
|
objs = []
|
|
faces = []
|
|
fns = []
|
|
normals = []
|
|
colors = []
|
|
|
|
obj = obj.splitlines()
|
|
for line in obj:
|
|
if line[0:2] == 'v ':
|
|
sl = line[2:].split()
|
|
verts.append((int(round(float(sl[0]))), int(round(float(sl[1]))), int(round(float(sl[2])))))
|
|
elif line[0:2] == 'l ':
|
|
sl = line[2:].split()
|
|
lines.append((int(sl[0])-1, int(sl[1])-1))
|
|
colors.append(activeColor)
|
|
elif line[0:3] == 'vn ':
|
|
sl = line[3:].split()
|
|
normals.append((float(sl[0]), float(sl[1]), float(sl[2])))
|
|
elif line[0:2] == 'f ':
|
|
if args.mode == 'facen':
|
|
sl = line[2:].split()
|
|
fn1 = sl[0].split('/')
|
|
fn2 = sl[1].split('/')
|
|
fn3 = sl[2].split('/')
|
|
faces.append((int(fn1[0])-1, int(fn2[0])-1, int(fn3[0])-1))
|
|
fns.append((int(fn1[2])-1, int(fn2[2])-1, int(fn3[2])-1))
|
|
colors.append(activeColor)
|
|
else:
|
|
sl = line[2:].split()
|
|
faces.append((int(sl[0])-1, int(sl[1])-1, int(sl[2])-1))
|
|
elif line[0:7] == 'usemtl ':
|
|
activeColor = line[7:]
|
|
|
|
output = f"#ifndef {args.name}_HC\n#define {args.name}_HC\n\n"
|
|
|
|
if args.mode == 'obj':
|
|
for li,line in enumerate(obj):
|
|
if line[0:2] == 'o ':
|
|
if (len(objs) > 0):
|
|
objs[len(objs)-1]["e"] = li
|
|
objs.append({"n": line[2:], "s": li})
|
|
objs[len(objs)-1]["e"] = len(obj);
|
|
for o in objs:
|
|
o["v"] = []
|
|
o["l"] = []
|
|
for i in range(o["s"]+1, o["e"]):
|
|
if obj[i][0:2] == 'v ':
|
|
sl = obj[i][2:].split()
|
|
o["v"].append((int(float(sl[0])), int(float(sl[1])), int(float(sl[2]))))
|
|
elif obj[i][0:2] == 'l ':
|
|
sl = obj[i][2:].split()
|
|
o["l"].append((int(sl[0])-1, int(sl[1])-1))
|
|
|
|
output = f"#define {args.name}_parts {len(objs)}\nI64 {args.name}_cnts[{len(objs)}];\nCD3I32 *{args.name}[{len(objs)}];\n"
|
|
for oi, o in enumerate(objs):
|
|
output += f"CD3I32 {args.name}_{oi}[{len(o['l'])*2}];\n"
|
|
i = 0
|
|
for li, line in enumerate(o['l']):
|
|
output += f"{args.name}_{oi}[{i}].x={o['v'][lines[li][0]][0]};{args.name}_{oi}[{i}].y={o['v'][lines[li][0]][1]};{args.name}_{oi}[{i}].z={o['v'][lines[li][0]][2]};\n"
|
|
i += 1
|
|
output += f"{args.name}_{oi}[{i}].x={o['v'][lines[li][1]][0]};{args.name}_{oi}[{i}].y={o['v'][lines[li][1]][1]};{args.name}_{oi}[{i}].z={o['v'][lines[li][1]][2]};\n"
|
|
i += 1
|
|
output += f"{args.name}_cnts[{oi}] = {len(o['l'])*2};\n{args.name}[{oi}] = {args.name}_{oi};\n"
|
|
elif args.mode == 'face':
|
|
if args.binary:
|
|
binOutput = bytes(len(faces).to_bytes(4, byteorder='little', signed=False))
|
|
for tri in faces:
|
|
binOutput += verts[tri[0]][0].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[0]][1].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[0]][2].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[1]][0].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[1]][1].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[1]][2].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[2]][0].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[2]][1].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[2]][2].to_bytes(4, byteorder='little', signed=True)
|
|
out = open(args.name, 'wb')
|
|
out.write(binOutput)
|
|
out.close()
|
|
else:
|
|
output += f"#define {args.name}_tris {len(faces)}\nCD3I32 {args.name}[{args.name}_tris][3];\n"
|
|
for ti, tri in enumerate(faces):
|
|
output += f"{args.name}[{ti}][0].x={verts[tri[0]][0]};{args.name}[{ti}][0].y={verts[tri[0]][1]};{args.name}[{ti}][0].z={verts[tri[0]][2]};\n";
|
|
output += f"{args.name}[{ti}][1].x={verts[tri[1]][0]};{args.name}[{ti}][1].y={verts[tri[1]][1]};{args.name}[{ti}][1].z={verts[tri[1]][2]};\n";
|
|
output += f"{args.name}[{ti}][2].x={verts[tri[2]][0]};{args.name}[{ti}][2].y={verts[tri[2]][1]};{args.name}[{ti}][2].z={verts[tri[2]][2]};\n";
|
|
elif args.mode == 'facen':
|
|
if args.binary:
|
|
binOutput = bytes(len(faces).to_bytes(2, byteorder='little', signed=False))
|
|
binOutput += len(normals).to_bytes(2, byteorder='little', signed=False)
|
|
assert len(faces) == len(fns)
|
|
assert len(faces) == len(colors)
|
|
for tri in faces:
|
|
binOutput += verts[tri[0]][0].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[0]][1].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[0]][2].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[1]][0].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[1]][1].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[1]][2].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[2]][0].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[2]][1].to_bytes(4, byteorder='little', signed=True)
|
|
binOutput += verts[tri[2]][2].to_bytes(4, byteorder='little', signed=True)
|
|
for ni in fns:
|
|
binOutput += ni[0].to_bytes(2, byteorder='little', signed=False)
|
|
for n in normals:
|
|
binOutput += struct.pack('<d', n[0])
|
|
binOutput += struct.pack('<d', n[1])
|
|
binOutput += struct.pack('<d', n[2])
|
|
for color in colors:
|
|
binOutput += COLOR_LOOKUP[color].to_bytes(1, byteorder='little', signed=False)
|
|
out = open(args.name, 'wb')
|
|
out.write(binOutput)
|
|
out.close()
|
|
else:
|
|
output += f"#include \"ModelTemplate.HC\"\n\n#define {args.name}TriCnt {len(faces)}\n#define {args.name}NormCnt {len(normals)}\nCD3I32 {args.name}Tri[{args.name}TriCnt][3]; // Triangles\nCD3 {args.name}N[{args.name}NormCnt]; // Normals\nI32 {args.name}NI[{args.name}TriCnt]; // Normal Index\nU8 {args.name}C[{args.name}TriCnt]; // Colors\nModelTemplate {args.name};\n"
|
|
for ti, tri in enumerate(faces):
|
|
output += f"{args.name}Tri[{ti}][0].x={verts[tri[0]][0]};{args.name}Tri[{ti}][0].y={verts[tri[0]][1]};{args.name}Tri[{ti}][0].z={verts[tri[0]][2]};\n";
|
|
output += f"{args.name}Tri[{ti}][1].x={verts[tri[1]][0]};{args.name}Tri[{ti}][1].y={verts[tri[1]][1]};{args.name}Tri[{ti}][1].z={verts[tri[1]][2]};\n";
|
|
output += f"{args.name}Tri[{ti}][2].x={verts[tri[2]][0]};{args.name}Tri[{ti}][2].y={verts[tri[2]][1]};{args.name}Tri[{ti}][2].z={verts[tri[2]][2]};\n";
|
|
for nii, ni in enumerate(fns):
|
|
output += f"{args.name}NI[{nii}]={ni[0]};\n";
|
|
for ni, n in enumerate(normals):
|
|
output += f"{args.name}N[{ni}].x={n[0]};{args.name}N[{ni}].y={n[1]};{args.name}N[{ni}].z={n[2]};\n";
|
|
for ci, color in enumerate(colors):
|
|
output += f"{args.name}C[{ci}]={color};\n";
|
|
output += f"{args.name}.triCnt={args.name}TriCnt;\n{args.name}.normCnt={args.name}NormCnt;\n{args.name}.tris={args.name}Tri;\n{args.name}.colors={args.name}C;\n{args.name}.normals={args.name}N;\n{args.name}.normIndex={args.name}NI;";
|
|
elif args.mode == 'collision':
|
|
# Collision mode expects a single contiguous loop of lines
|
|
# The first line (idx 0) is expected to contain the first vert (idx 0)
|
|
# The first line+first vert determines if wrap is clockwise or counter-clockwise
|
|
cverts = []
|
|
cedges = []
|
|
v0 = 0
|
|
v_last = v0
|
|
vc = 0
|
|
vc_last = 0
|
|
|
|
# check first edge, swap if needed
|
|
fv = lines[0][0]
|
|
if lines[0][1] == 0:
|
|
fv = lines[0][1]
|
|
cverts.append((verts[fv][0], verts[fv][1], verts[fv][2]))
|
|
|
|
while True:
|
|
found = False
|
|
if vc >= len(verts)-1:
|
|
break
|
|
for i, e in enumerate(lines):
|
|
if e[0] == v0:
|
|
if e[1] == v_last:
|
|
continue
|
|
v_last = v0
|
|
v0 = e[1]
|
|
vc_last = vc
|
|
vc += 1
|
|
cverts.append((verts[e[1]][0], verts[e[1]][1], verts[e[1]][2]))
|
|
cedges.append((vc_last, vc))
|
|
found = True
|
|
break
|
|
elif e[1] == v0:
|
|
if e[0] == v_last:
|
|
continue
|
|
v_last = v0
|
|
v0 = e[0]
|
|
vc_last = vc
|
|
vc += 1
|
|
cverts.append((verts[e[0]][0], verts[e[0]][1], verts[e[0]][2]))
|
|
cedges.append((vc_last, vc))
|
|
found = True
|
|
break
|
|
if found == False:
|
|
print("Not Found", v0)
|
|
break
|
|
cedges.append((vc, 0))
|
|
|
|
lineArrB = bytes((len(cedges)+1).to_bytes(4, byteorder='little', signed=False))
|
|
for li,line in enumerate(lines):
|
|
lineArrB += cverts[cedges[li][0]][0].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += cverts[cedges[li][0]][1].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += cverts[cedges[li][0]][2].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += cverts[cedges[0][0]][0].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += cverts[cedges[0][0]][1].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += cverts[cedges[0][0]][2].to_bytes(4, byteorder='little', signed=True)
|
|
out = open(args.name, 'wb')
|
|
out.write(lineArrB)
|
|
out.close()
|
|
if args.debug:
|
|
print('=verts=')
|
|
for v in cverts:
|
|
print(v)
|
|
print('=lines=')
|
|
for v in cedges:
|
|
print(v)
|
|
else: # lines
|
|
if args.binary:
|
|
lineArrB = bytes((len(lines)*2).to_bytes(4, byteorder='little', signed=False))
|
|
for li,line in enumerate(lines):
|
|
lineArrB += verts[lines[li][0]][0].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += verts[lines[li][0]][1].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += verts[lines[li][0]][2].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += verts[lines[li][1]][0].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += verts[lines[li][1]][1].to_bytes(4, byteorder='little', signed=True)
|
|
lineArrB += verts[lines[li][1]][2].to_bytes(4, byteorder='little', signed=True)
|
|
if args.color:
|
|
for color in colors:
|
|
lineArrB += COLOR_LOOKUP[color].to_bytes(1, byteorder='little', signed=False)
|
|
out = open(args.name, 'wb')
|
|
out.write(lineArrB)
|
|
out.close()
|
|
else:
|
|
output += f"#define {args.name}_cnt {len(lines)*2}\nCD3I32 {args.name}[{args.name}_cnt];\n"
|
|
i = 0
|
|
for li,line in enumerate(lines):
|
|
output += f"{args.name}[{i}].x={verts[lines[li][0]][0]};{args.name}[{i}].y={verts[lines[li][0]][1]};{args.name}[{i}].z={verts[lines[li][0]][2]};\n"
|
|
i += 1
|
|
output += f"{args.name}[{i}].x={verts[lines[li][1]][0]};{args.name}[{i}].y={verts[lines[li][1]][1]};{args.name}[{i}].z={verts[lines[li][1]][2]};\n"
|
|
i += 1
|
|
|
|
if not args.binary:
|
|
output += "\n#endif"
|
|
print(output)
|