correct line endings

This commit is contained in:
fgsfds 2020-05-16 07:36:28 +03:00
parent ee69a027ef
commit 8b71a1a734
2 changed files with 246 additions and 246 deletions

280
c2obj.py
View file

@ -1,141 +1,141 @@
""" """
This module attempts to parse the ``model.inc.c`` files and extract the This module attempts to parse the ``model.inc.c`` files and extract the
3D models within as standard Wavefront OBJ files. 3D models within as standard Wavefront OBJ files.
Example: Example:
Specify the path to the ``.inc.c`` file and a directory where to save Specify the path to the ``.inc.c`` file and a directory where to save
the extracted ``.obj`` files. the extracted ``.obj`` files.
$ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/ $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
This is a work in progress and it currently has some serious limitations: This is a work in progress and it currently has some serious limitations:
* It only extracts geometry information, so no textures or any other info * It only extracts geometry information, so no textures or any other info
* It makes assumptions about the layout of the code in the C source * It makes assumptions about the layout of the code in the C source
* It hasn't been properly tested. * It hasn't been properly tested.
""" """
def parse(filename, output_directory): def parse(filename, output_directory):
from os import path, mkdir from os import path, mkdir
if not path.isdir(output_directory): if not path.isdir(output_directory):
try: try:
mkdir(output_directory) mkdir(output_directory)
except OSError: except OSError:
print(f'Could not use output directory {output_directory}.') print(f'Could not use output directory {output_directory}.')
vtx_def = 'static const Vtx ' vtx_def = 'static const Vtx '
vtx_data = {} vtx_data = {}
reading_vtx = False reading_vtx = False
current_vtx_name = '' current_vtx_name = ''
current_vtx_data = [] current_vtx_data = []
current_vtx_vertices = 0 current_vtx_vertices = 0
gfx_def = 'const Gfx ' gfx_def = 'const Gfx '
reading_gfx = False reading_gfx = False
current_gfx_vertices = 0 current_gfx_vertices = 0
current_gfx_faces = 0 current_gfx_faces = 0
insert_vert_call = 'gsSPVertex(' insert_vert_call = 'gsSPVertex('
insert_1tri_call = 'gsSP1Triangle(' insert_1tri_call = 'gsSP1Triangle('
insert_2tri_call = 'gsSP2Triangles(' insert_2tri_call = 'gsSP2Triangles('
gfx_count = 0 gfx_count = 0
end_of_block = '};' end_of_block = '};'
with open(filename, 'r') as f: with open(filename, 'r') as f:
for line in f: for line in f:
line = line.strip() line = line.strip()
if line.startswith(vtx_def): if line.startswith(vtx_def):
vtx_name = line.split(' ')[3][:-2] vtx_name = line.split(' ')[3][:-2]
current_vtx_name = vtx_name current_vtx_name = vtx_name
current_vtx_data = [] current_vtx_data = []
reading_vtx = True reading_vtx = True
continue continue
if line.startswith(gfx_def): if line.startswith(gfx_def):
from datetime import datetime from datetime import datetime
current_gfx_name = line.split(' ')[2][:-2] current_gfx_name = line.split(' ')[2][:-2]
current_gfx_file = open(path.join(output_directory, current_gfx_name + '.obj'), 'w') current_gfx_file = open(path.join(output_directory, current_gfx_name + '.obj'), 'w')
current_gfx_file.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n") current_gfx_file.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n")
current_gfx_file.write('# File Created: {}\n\n'.format(datetime.now())) current_gfx_file.write('# File Created: {}\n\n'.format(datetime.now()))
reading_gfx = True reading_gfx = True
continue continue
if line == end_of_block: if line == end_of_block:
if reading_vtx: if reading_vtx:
vtx_data[current_vtx_name] = current_vtx_data vtx_data[current_vtx_name] = current_vtx_data
reading_vtx = False reading_vtx = False
elif reading_gfx: elif reading_gfx:
current_gfx_file.write(f'# {current_gfx_faces} faces\n\n') current_gfx_file.write(f'# {current_gfx_faces} faces\n\n')
current_gfx_file.close() current_gfx_file.close()
current_gfx_vertices = 0 current_gfx_vertices = 0
reading_gfx = False reading_gfx = False
gfx_count += 1 gfx_count += 1
continue continue
if reading_vtx: if reading_vtx:
line = line.replace('{', '[').replace('}', ']') line = line.replace('{', '[').replace('}', ']')
tri = eval(line[:-1])[0] tri = eval(line[:-1])[0]
current_vtx_data.append(tri) current_vtx_data.append(tri)
continue continue
if reading_gfx: if reading_gfx:
if line.startswith(insert_vert_call): if line.startswith(insert_vert_call):
args = line[len(insert_vert_call):].split(',') args = line[len(insert_vert_call):].split(',')
current_vtx_name = args[0] current_vtx_name = args[0]
if current_gfx_vertices > 0: if current_gfx_vertices > 0:
current_gfx_file.write(f'# {current_gfx_faces} faces\n\n') current_gfx_file.write(f'# {current_gfx_faces} faces\n\n')
current_gfx_faces = 0 current_gfx_faces = 0
current_vtx_vertices = len(vtx_data[current_vtx_name]) current_vtx_vertices = len(vtx_data[current_vtx_name])
current_gfx_vertices += current_vtx_vertices current_gfx_vertices += current_vtx_vertices
current_gfx_file.write(f'#\n# object {current_vtx_name}\n#\n\n') current_gfx_file.write(f'#\n# object {current_vtx_name}\n#\n\n')
current_vtx_data = vtx_data[current_vtx_name] current_vtx_data = vtx_data[current_vtx_name]
for tri in current_vtx_data: for tri in current_vtx_data:
v = tri[0] v = tri[0]
current_gfx_file.write('v {:.3f} {:.3f} {:.3f}\n'.format(*v)) current_gfx_file.write('v {:.3f} {:.3f} {:.3f}\n'.format(*v))
current_gfx_file.write(f'# {current_vtx_vertices} vertices\n\n') current_gfx_file.write(f'# {current_vtx_vertices} vertices\n\n')
for tri in current_vtx_data: for tri in current_vtx_data:
n = [_decode_normal(u) for u in tri[3][:3]] n = [_decode_normal(u) for u in tri[3][:3]]
current_gfx_file.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n)) current_gfx_file.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n))
current_gfx_file.write(f'# {current_vtx_vertices} vertex normals\n\n') current_gfx_file.write(f'# {current_vtx_vertices} vertex normals\n\n')
current_gfx_file.write(f'g {current_vtx_name}\n\n') current_gfx_file.write(f'g {current_vtx_name}\n\n')
elif line.startswith(insert_2tri_call): elif line.startswith(insert_2tri_call):
args = line[len(insert_2tri_call):].split(',') args = line[len(insert_2tri_call):].split(',')
correction = current_gfx_vertices - current_vtx_vertices + 1 correction = current_gfx_vertices - current_vtx_vertices + 1
indexes = [eval(args[i]) + correction for i in [0, 1, 2, 4, 5, 6]] indexes = [eval(args[i]) + correction for i in [0, 1, 2, 4, 5, 6]]
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3])) current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3]))
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:])) current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:]))
current_gfx_faces += 2 current_gfx_faces += 2
elif line.startswith(insert_1tri_call): elif line.startswith(insert_1tri_call):
args = line[len(insert_1tri_call):].split(',') args = line[len(insert_1tri_call):].split(',')
correction = current_gfx_vertices - current_vtx_vertices + 1 correction = current_gfx_vertices - current_vtx_vertices + 1
indexes = [eval(args[i]) + correction for i in [0, 1, 2]] indexes = [eval(args[i]) + correction for i in [0, 1, 2]]
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes)) current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes))
current_gfx_faces += 1 current_gfx_faces += 1
continue continue
print(f'{gfx_count} models extracted.') print(f'{gfx_count} models extracted.')
def _decode_normal(x): def _decode_normal(x):
y = x if x <= 127 else x - 255 y = x if x <= 127 else x - 255
return y / 127 return y / 127
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filename', help = 'filename of the .inc.c source file') parser.add_argument('filename', help = 'filename of the .inc.c source file')
parser.add_argument('output_directory', help = 'directory where to put the extracted .obj files') parser.add_argument('output_directory', help = 'directory where to put the extracted .obj files')
args = parser.parse_args() args = parser.parse_args()
parse(args.filename, args.output_directory) parse(args.filename, args.output_directory)

212
obj2c.py
View file

@ -1,107 +1,107 @@
""" """
This module generates a fragment of C code, in the style of that found in This module generates a fragment of C code, in the style of that found in
the ``model.inc.c`` files, that encodes the geometry of the model specified the ``model.inc.c`` files, that encodes the geometry of the model specified
by the Wavefront OBJ file. by the Wavefront OBJ file.
Example: Example:
Specify the path to the ``.obj`` file and pipe the output of the script Specify the path to the ``.obj`` file and pipe the output of the script
into the desired destination ``.c`` file. into the desired destination ``.c`` file.
$ python obj2c.py left_hand_closed.obj > left_hand_closed.inc.c $ python obj2c.py left_hand_closed.obj > left_hand_closed.inc.c
This is a work in progress and it currently has some serious limitations: This is a work in progress and it currently has some serious limitations:
* It only encodes the geometry information of the OBJ file, so no * It only encodes the geometry information of the OBJ file, so no
texture mapping or any other info. texture mapping or any other info.
* The generated fragment of C code has to be manually pasted into the * The generated fragment of C code has to be manually pasted into the
desired source file. Make sure that the name of the Gfx structure desired source file. Make sure that the name of the Gfx structure
you're pasting matches the one you're replacing. you're pasting matches the one you're replacing.
* It hasn't been properly tested. * It hasn't been properly tested.
""" """
def parse(filename): def parse(filename):
from os.path import basename, splitext from os.path import basename, splitext
from re import sub from re import sub
# WARNIGN! # WARNIGN!
# `gfx_name` is just a guess. You have to manually check that the name # `gfx_name` is just a guess. You have to manually check that the name
# of the Gfx structure you're pasting matches the one you're replacing. # of the Gfx structure you're pasting matches the one you're replacing.
clean = lambda fn: sub('\W|^(?=\d)','_', fn) clean = lambda fn: sub('\W|^(?=\d)','_', fn)
gfx_name = clean(splitext(basename(filename))[0]) gfx_name = clean(splitext(basename(filename))[0])
gfx_vertices = [] gfx_vertices = []
gfx_normals = [] gfx_normals = []
vertex_to_normal = {} vertex_to_normal = {}
gfx_v_count = 0 gfx_v_count = 0
vtx_name = '' vtx_name = ''
vtx_faces = [] vtx_faces = []
vtx_v_count = 0 vtx_v_count = 0
output_upper = [] output_upper = []
output_lower = [f'const Gfx {gfx_name}[] = {{'] output_lower = [f'const Gfx {gfx_name}[] = {{']
with open(filename, 'r') as obj: with open(filename, 'r') as obj:
for line in obj: for line in obj:
line = line.strip() line = line.strip()
if line.startswith('v '): if line.startswith('v '):
coordinates = [eval(x) for x in line.split()[1:4]] coordinates = [eval(x) for x in line.split()[1:4]]
gfx_vertices.append(coordinates) gfx_vertices.append(coordinates)
vtx_v_count += 1 vtx_v_count += 1
gfx_v_count += 1 gfx_v_count += 1
if line.startswith('vn '): if line.startswith('vn '):
coordinates = [eval(x) for x in line.split()[1:4]] coordinates = [eval(x) for x in line.split()[1:4]]
gfx_normals.append([_encode_normal(x) for x in coordinates]) gfx_normals.append([_encode_normal(x) for x in coordinates])
if line.startswith('g '): if line.startswith('g '):
vtx_name = line.split()[1] vtx_name = line.split()[1]
if line.startswith('f '): if line.startswith('f '):
pairs = [pair.split('//') for pair in line.split()[1:4]] pairs = [pair.split('//') for pair in line.split()[1:4]]
vtx_faces.append([int(pair[0]) for pair in pairs]) vtx_faces.append([int(pair[0]) for pair in pairs])
for (x, y) in pairs: for (x, y) in pairs:
vertex_to_normal[int(x) - 1] = int(y) - 1 vertex_to_normal[int(x) - 1] = int(y) - 1
if line.startswith('# ') and line.endswith('faces'): if line.startswith('# ') and line.endswith('faces'):
output_upper.append(f'static const Vtx {vtx_name}[] = {{') output_upper.append(f'static const Vtx {vtx_name}[] = {{')
for i in range(gfx_v_count - vtx_v_count, gfx_v_count): for i in range(gfx_v_count - vtx_v_count, gfx_v_count):
v_string = '[{}, {}, {}]'.format(*gfx_vertices[i]) v_string = '[{}, {}, {}]'.format(*gfx_vertices[i])
n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]]) n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]])
combined = f' [[{v_string}, 0, [0, 0], {n_string}]],' combined = f' [[{v_string}, 0, [0, 0], {n_string}]],'
output_upper.append(combined.replace('[', '{').replace(']', '}')) output_upper.append(combined.replace('[', '{').replace(']', '}'))
output_upper.append('};\n') output_upper.append('};\n')
output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),') output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),')
n = len(vtx_faces) n = len(vtx_faces)
correction = vtx_v_count - gfx_v_count - 1 correction = vtx_v_count - gfx_v_count - 1
for i in range(int(n / 2)): for i in range(int(n / 2)):
f1 = [vtx_faces[2 * i][j] + correction for j in range(3)] f1 = [vtx_faces[2 * i][j] + correction for j in range(3)]
f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)] f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)]
output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2)) output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2))
if n % 2 != 0: if n % 2 != 0:
f3 = [vtx_faces[-1][j] + correction for j in range(3)] f3 = [vtx_faces[-1][j] + correction for j in range(3)]
output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3)) output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3))
vtx_v_count = 0 vtx_v_count = 0
vtx_faces = [] vtx_faces = []
output_lower.append(' gsSPEndDisplayList(),') output_lower.append(' gsSPEndDisplayList(),')
output_lower.append('};') output_lower.append('};')
for line in output_upper + output_lower: for line in output_upper + output_lower:
print(line) print(line)
def _encode_normal(x): def _encode_normal(x):
x *= 127 x *= 127
if x <= 0: x += 255 if x <= 0: x += 255
return hex(int(x)) return hex(int(x))
if __name__ == "__main__": if __name__ == "__main__":
import argparse import argparse
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filename', help = 'filename of the .obj file to parse') parser.add_argument('filename', help = 'filename of the .obj file to parse')
args = parser.parse_args() args = parser.parse_args()
parse(args.filename) parse(args.filename)