11"""
22This module attempts to parse the ``model.inc.c`` files and extract the
3- 3D models within as standard Wavefront OBJ files.
4-
3+ 3D models (including normals and texture coordinates) within as standard
4+ Wavefront OBJ files.
55Example:
66 Specify the path to the ``.inc.c`` file and a directory where to save
77 the extracted ``.obj`` files.
8-
98 $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
10-
119This 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
1310 * It makes assumptions about the layout of the code in the C source
11+ * It assumes that the encoded texture coordinates are for a 32x32 map
1412 * It hasn't been properly tested.
15-
1613"""
1714
1815def parse (filename , output_directory ):
@@ -23,7 +20,7 @@ def parse(filename, output_directory):
2320 mkdir (output_directory )
2421 except OSError :
2522 print (f'Could not use output directory { output_directory } .' )
26-
23+
2724 vtx_def = 'static const Vtx '
2825 vtx_data = {}
2926 reading_vtx = False
@@ -55,14 +52,14 @@ def parse(filename, output_directory):
5552
5653 if line .startswith (gfx_def ):
5754 from datetime import datetime
58-
55+
5956 current_gfx_name = line .split (' ' )[2 ][:- 2 ]
6057 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 ()) )
58+ current_gfx_file .write (f "# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n " )
59+ current_gfx_file .write (f '# File Created: { datetime . now () } \n \n ' )
6360 reading_gfx = True
6461 continue
65-
62+
6663 if line == end_of_block :
6764 if reading_vtx :
6865 vtx_data [current_vtx_name ] = current_vtx_data
@@ -74,23 +71,23 @@ def parse(filename, output_directory):
7471 current_gfx_vertices = 0
7572 reading_gfx = False
7673 gfx_count += 1
77-
74+
7875 continue
79-
76+
8077 if reading_vtx :
8178 line = line .replace ('{' , '[' ).replace ('}' , ']' )
8279 tri = eval (line [:- 1 ])[0 ]
8380 current_vtx_data .append (tri )
8481 continue
85-
82+
8683 if reading_gfx :
8784 if line .startswith (insert_vert_call ):
8885 args = line [len (insert_vert_call ):].split (',' )
8986 current_vtx_name = args [0 ]
90-
87+
9188 if current_gfx_vertices > 0 :
9289 current_gfx_file .write (f'# { current_gfx_faces } faces\n \n ' )
93-
90+
9491 current_gfx_faces = 0
9592 current_vtx_vertices = len (vtx_data [current_vtx_name ])
9693 current_gfx_vertices += current_vtx_vertices
@@ -107,31 +104,53 @@ def parse(filename, output_directory):
107104 current_gfx_file .write ('vn {:.3f} {:.3f} {:.3f}\n ' .format (* n ))
108105 current_gfx_file .write (f'# { current_vtx_vertices } vertex normals\n \n ' )
109106
107+ texture_info = any (_has_texture_info (tri ) for tri in current_vtx_data )
108+ if texture_info :
109+ for tri in current_vtx_data :
110+ u = _decode_texture (tri [2 ][0 ])
111+ v = 1 - _decode_texture (tri [2 ][1 ])
112+ current_gfx_file .write ('vt {:.3f} {:.3f}\n ' .format (u , v ))
113+ current_gfx_file .write (f'# { current_vtx_vertices } texture coords\n \n ' )
114+
110115 current_gfx_file .write (f'g { current_vtx_name } \n \n ' )
111-
116+
112117 elif line .startswith (insert_2tri_call ):
113118 args = line [len (insert_2tri_call ):].split (',' )
114119 correction = current_gfx_vertices - current_vtx_vertices + 1
115120 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 :]))
121+ if texture_info :
122+ current_gfx_file .write ('f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n ' .format (* indexes [:3 ]))
123+ current_gfx_file .write ('f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n ' .format (* indexes [3 :]))
124+ else :
125+ current_gfx_file .write ('f {0}//{0} {1}//{1} {2}//{2}\n ' .format (* indexes [:3 ]))
126+ current_gfx_file .write ('f {0}//{0} {1}//{1} {2}//{2}\n ' .format (* indexes [3 :]))
118127 current_gfx_faces += 2
119128
120129 elif line .startswith (insert_1tri_call ):
121130 args = line [len (insert_1tri_call ):].split (',' )
122131 correction = current_gfx_vertices - current_vtx_vertices + 1
123132 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 ))
133+ if texture_info :
134+ current_gfx_file .write ('f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n ' .format (* indexes ))
135+ else :
136+ current_gfx_file .write ('f {0}//{0} {1}//{1} {2}//{2}\n ' .format (* indexes ))
125137 current_gfx_faces += 1
126138
127139 continue
128-
140+
129141 print (f'{ gfx_count } models extracted.' )
130142
143+ def _has_texture_info (tri ):
144+ (u , v ) = tri [2 ]
145+ return int (u ) != 0 or int (v ) != 0
146+
131147def _decode_normal (x ):
132148 y = x if x <= 127 else x - 255
133149 return y / 127
134150
151+ def _decode_texture (x ):
152+ return x / (2 ** 10 )
153+
135154if __name__ == "__main__" :
136155 import argparse
137156 parser = argparse .ArgumentParser ()
0 commit comments