1
- """
2
- This module attempts to parse the ``model.inc.c`` files and extract the
3
- 3D models within as standard Wavefront OBJ files.
4
-
5
- Example:
6
- Specify the path to the ``.inc.c`` file and a directory where to save
7
- the extracted ``.obj`` files.
8
-
9
- $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
10
-
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.
15
-
16
- """
17
-
18
- def parse (filename , output_directory ):
19
- from os import path , mkdir
20
-
21
- if not path .isdir (output_directory ):
22
- try :
23
- mkdir (output_directory )
24
- except OSError :
25
- print (f'Could not use output directory { output_directory } .' )
26
-
27
- vtx_def = 'static const Vtx '
28
- vtx_data = {}
29
- reading_vtx = False
30
- current_vtx_name = ''
31
- current_vtx_data = []
32
- current_vtx_vertices = 0
33
-
34
- gfx_def = 'const Gfx '
35
- reading_gfx = False
36
- current_gfx_vertices = 0
37
- current_gfx_faces = 0
38
- insert_vert_call = 'gsSPVertex('
39
- insert_1tri_call = 'gsSP1Triangle('
40
- insert_2tri_call = 'gsSP2Triangles('
41
- gfx_count = 0
42
-
43
- end_of_block = '};'
44
-
45
- with open (filename , 'r' ) as f :
46
- for line in f :
47
- line = line .strip ()
48
-
49
- if line .startswith (vtx_def ):
50
- vtx_name = line .split (' ' )[3 ][:- 2 ]
51
- current_vtx_name = vtx_name
52
- current_vtx_data = []
53
- reading_vtx = True
54
- continue
55
-
56
- if line .startswith (gfx_def ):
57
- from datetime import datetime
58
-
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 ()))
63
- reading_gfx = True
64
- continue
65
-
66
- if line == end_of_block :
67
- if reading_vtx :
68
- vtx_data [current_vtx_name ] = current_vtx_data
69
- reading_vtx = False
70
-
71
- elif reading_gfx :
72
- current_gfx_file .write (f'# { current_gfx_faces } faces\n \n ' )
73
- current_gfx_file .close ()
74
- current_gfx_vertices = 0
75
- reading_gfx = False
76
- gfx_count += 1
77
-
78
- continue
79
-
80
- if reading_vtx :
81
- line = line .replace ('{' , '[' ).replace ('}' , ']' )
82
- tri = eval (line [:- 1 ])[0 ]
83
- current_vtx_data .append (tri )
84
- continue
85
-
86
- if reading_gfx :
87
- if line .startswith (insert_vert_call ):
88
- args = line [len (insert_vert_call ):].split (',' )
89
- current_vtx_name = args [0 ]
90
-
91
- if current_gfx_vertices > 0 :
92
- current_gfx_file .write (f'# { current_gfx_faces } faces\n \n ' )
93
-
94
- current_gfx_faces = 0
95
- current_vtx_vertices = len (vtx_data [current_vtx_name ])
96
- current_gfx_vertices += current_vtx_vertices
97
-
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 :
101
- v = tri [0 ]
102
- current_gfx_file .write ('v {:.3f} {:.3f} {:.3f}\n ' .format (* v ))
103
- current_gfx_file .write (f'# { current_vtx_vertices } vertices\n \n ' )
104
-
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 ' )
109
-
110
- current_gfx_file .write (f'g { current_vtx_name } \n \n ' )
111
-
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
119
-
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
126
-
127
- continue
128
-
129
- print (f'{ gfx_count } models extracted.' )
130
-
131
- def _decode_normal (x ):
132
- y = x if x <= 127 else x - 255
133
- return y / 127
134
-
135
- if __name__ == "__main__" :
136
- import argparse
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 ()
1
+ """
2
+ This module attempts to parse the ``model.inc.c`` files and extract the
3
+ 3D models within as standard Wavefront OBJ files.
4
+
5
+ Example:
6
+ Specify the path to the ``.inc.c`` file and a directory where to save
7
+ the extracted ``.obj`` files.
8
+
9
+ $ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
10
+
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.
15
+
16
+ """
17
+
18
+ def parse (filename , output_directory ):
19
+ from os import path , mkdir
20
+
21
+ if not path .isdir (output_directory ):
22
+ try :
23
+ mkdir (output_directory )
24
+ except OSError :
25
+ print (f'Could not use output directory { output_directory } .' )
26
+
27
+ vtx_def = 'static const Vtx '
28
+ vtx_data = {}
29
+ reading_vtx = False
30
+ current_vtx_name = ''
31
+ current_vtx_data = []
32
+ current_vtx_vertices = 0
33
+
34
+ gfx_def = 'const Gfx '
35
+ reading_gfx = False
36
+ current_gfx_vertices = 0
37
+ current_gfx_faces = 0
38
+ insert_vert_call = 'gsSPVertex('
39
+ insert_1tri_call = 'gsSP1Triangle('
40
+ insert_2tri_call = 'gsSP2Triangles('
41
+ gfx_count = 0
42
+
43
+ end_of_block = '};'
44
+
45
+ with open (filename , 'r' ) as f :
46
+ for line in f :
47
+ line = line .strip ()
48
+
49
+ if line .startswith (vtx_def ):
50
+ vtx_name = line .split (' ' )[3 ][:- 2 ]
51
+ current_vtx_name = vtx_name
52
+ current_vtx_data = []
53
+ reading_vtx = True
54
+ continue
55
+
56
+ if line .startswith (gfx_def ):
57
+ from datetime import datetime
58
+
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 ()))
63
+ reading_gfx = True
64
+ continue
65
+
66
+ if line == end_of_block :
67
+ if reading_vtx :
68
+ vtx_data [current_vtx_name ] = current_vtx_data
69
+ reading_vtx = False
70
+
71
+ elif reading_gfx :
72
+ current_gfx_file .write (f'# { current_gfx_faces } faces\n \n ' )
73
+ current_gfx_file .close ()
74
+ current_gfx_vertices = 0
75
+ reading_gfx = False
76
+ gfx_count += 1
77
+
78
+ continue
79
+
80
+ if reading_vtx :
81
+ line = line .replace ('{' , '[' ).replace ('}' , ']' )
82
+ tri = eval (line [:- 1 ])[0 ]
83
+ current_vtx_data .append (tri )
84
+ continue
85
+
86
+ if reading_gfx :
87
+ if line .startswith (insert_vert_call ):
88
+ args = line [len (insert_vert_call ):].split (',' )
89
+ current_vtx_name = args [0 ]
90
+
91
+ if current_gfx_vertices > 0 :
92
+ current_gfx_file .write (f'# { current_gfx_faces } faces\n \n ' )
93
+
94
+ current_gfx_faces = 0
95
+ current_vtx_vertices = len (vtx_data [current_vtx_name ])
96
+ current_gfx_vertices += current_vtx_vertices
97
+
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 :
101
+ v = tri [0 ]
102
+ current_gfx_file .write ('v {:.3f} {:.3f} {:.3f}\n ' .format (* v ))
103
+ current_gfx_file .write (f'# { current_vtx_vertices } vertices\n \n ' )
104
+
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 ' )
109
+
110
+ current_gfx_file .write (f'g { current_vtx_name } \n \n ' )
111
+
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
119
+
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
126
+
127
+ continue
128
+
129
+ print (f'{ gfx_count } models extracted.' )
130
+
131
+ def _decode_normal (x ):
132
+ y = x if x <= 127 else x - 255
133
+ return y / 127
134
+
135
+ if __name__ == "__main__" :
136
+ import argparse
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
141
parse (args .filename , args .output_directory )
0 commit comments