2 This module attempts to parse the ``model.inc.c`` files and extract the
3 3D models within as standard Wavefront OBJ files.
6 Specify the path to the ``.inc.c`` file and a directory where to save
7 the extracted ``.obj`` files.
9 $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
11 This is a work in progress and it currently has some serious limitations:
12 * It only extracts geometry information, so no textures or any other info
13 * It makes assumptions about the layout of the code in the C source
14 * It hasn't been properly tested.
18 def parse(filename
, output_directory
):
19 from os
import path
, mkdir
21 if not path
.isdir(output_directory
):
23 mkdir(output_directory
)
25 print(f
'Could not use output directory {output_directory}.')
27 vtx_def
= 'static const Vtx '
32 current_vtx_vertices
= 0
34 gfx_def
= 'const Gfx '
36 current_gfx_vertices
= 0
38 insert_vert_call
= 'gsSPVertex('
39 insert_1tri_call
= 'gsSP1Triangle('
40 insert_2tri_call
= 'gsSP2Triangles('
45 with
open(filename
, 'r') as f
:
49 if line
.startswith(vtx_def
):
50 vtx_name
= line
.split(' ')[3][:-2]
51 current_vtx_name
= vtx_name
56 if line
.startswith(gfx_def
):
57 from datetime
import datetime
59 current_gfx_name
= line
.split(' ')[2][:-2]
60 current_gfx_file
= open(path
.join(output_directory
, current_gfx_name
+ '.obj'), 'w')
61 current_gfx_file
.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n")
62 current_gfx_file
.write('# File Created: {}\n\n'.format(datetime
.now()))
66 if line
== end_of_block
:
68 vtx_data
[current_vtx_name
] = current_vtx_data
72 current_gfx_file
.write(f
'# {current_gfx_faces} faces\n\n')
73 current_gfx_file
.close()
74 current_gfx_vertices
= 0
81 line
= line
.replace('{', '[').replace('}', ']')
82 tri
= eval(line
[:-1])[0]
83 current_vtx_data
.append(tri
)
87 if line
.startswith(insert_vert_call
):
88 args
= line
[len(insert_vert_call
):].split(',')
89 current_vtx_name
= args
[0]
91 if current_gfx_vertices
> 0:
92 current_gfx_file
.write(f
'# {current_gfx_faces} faces\n\n')
95 current_vtx_vertices
= len(vtx_data
[current_vtx_name
])
96 current_gfx_vertices
+= current_vtx_vertices
98 current_gfx_file
.write(f
'#\n# object {current_vtx_name}\n#\n\n')
99 current_vtx_data
= vtx_data
[current_vtx_name
]
100 for tri
in current_vtx_data
:
102 current_gfx_file
.write('v {:.3f} {:.3f} {:.3f}\n'.format(*v
))
103 current_gfx_file
.write(f
'# {current_vtx_vertices} vertices\n\n')
105 for tri
in current_vtx_data
:
106 n
= [_decode_normal(u
) for u
in tri
[3][:3]]
107 current_gfx_file
.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n
))
108 current_gfx_file
.write(f
'# {current_vtx_vertices} vertex normals\n\n')
110 current_gfx_file
.write(f
'g {current_vtx_name}\n\n')
112 elif line
.startswith(insert_2tri_call
):
113 args
= line
[len(insert_2tri_call
):].split(',')
114 correction
= current_gfx_vertices
- current_vtx_vertices
+ 1
115 indexes
= [eval(args
[i
]) + correction
for i
in [0, 1, 2, 4, 5, 6]]
116 current_gfx_file
.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes
[:3]))
117 current_gfx_file
.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes
[3:]))
118 current_gfx_faces
+= 2
120 elif line
.startswith(insert_1tri_call
):
121 args
= line
[len(insert_1tri_call
):].split(',')
122 correction
= current_gfx_vertices
- current_vtx_vertices
+ 1
123 indexes
= [eval(args
[i
]) + correction
for i
in [0, 1, 2]]
124 current_gfx_file
.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes
))
125 current_gfx_faces
+= 1
129 print(f
'{gfx_count} models extracted.')
131 def _decode_normal(x
):
132 y
= x
if x
<= 127 else x
- 255
135 if __name__
== "__main__":
137 parser
= argparse
.ArgumentParser()
138 parser
.add_argument('filename', help = 'filename of the .inc.c source file')
139 parser
.add_argument('output_directory', help = 'directory where to put the extracted .obj files')
140 args
= parser
.parse_args()
141 parse(args
.filename
, args
.output_directory
)