Completed
Push — master ( 6e854b...567a5e )
by
unknown
54s
created

ed2d.assets.OBJ.write()   A

Complexity

Conditions 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 4
dl 0
loc 14
rs 9.2
1
2
from ed2d.mesh import MeshBase
3
from ed2d.assets.mtlloader import MTL
4
from ed2d import files
5
6
class OBJ(object):
7
    def __init__(self, fileName):
8
        ''' Wavefront .obj file parser.'''
9
        fcount = 0
10
        vcount = 0
11
        vtcount = 0
12
        vncount = 0
13
        gcount = 0
14
15
        self.tmvnig = {} # Indices dictionary
16
        self.fmvnig = {} # Final data dictionary
17
18
        __objpath = files.resolve_path('data', 'models', fileName + '.obj')
19
        __mtlpath = files.resolve_path('data', 'models', fileName + '.mtl')
20
        __txtpath = files.resolve_path('data', 'models', fileName + '.txt')
21
22
        # Load the mtl file
23
        self.mtlfile = MTL(__mtlpath)
24
25
        # Load the obj file
26
        objfile = open(__objpath, "r")
27
28
        lines = objfile.readlines()
29
        lineslen = len(lines)
30
31
        # Do a head count and setup the storage layout using dictionaries
32
        for i in range(lineslen):
33
            value = lines[i][:2]
34
            if value == 'v ':
35
                vcount += 1
36
            elif value == 'vt':
37
                vtcount += 1
38
            elif value == 'vn':
39
                vncount += 1
40
            elif value == 'g ':
41
                gcount += 1
42
                # The material used currently
43
                materialName = None
44
                if lines[i+1][:6] == 'usemtl':
45
                    materialName = lines[i+1].split()[1]
46
                    # Initialize the two dictionaries, indices and final data
47
                    self.tmvnig[materialName] = [ [], [], [] ]
48
                    self.fmvnig[materialName] = [ [], [], [] ]
49
50
                for j in range(i+2, lineslen, 1):
51
                    fval = lines[j][:2]
52
53
                    if fval == 'f ':
54
                        fcount += 1
55
                        # Vertices
56
                        self.tmvnig[materialName][0].extend(None for _ in range(3))
57
                        self.fmvnig[materialName][0].extend([None] for _ in range(3))
58
                        # Normals
59
                        self.tmvnig[materialName][2].extend(None for _ in range(3))
60
                        self.fmvnig[materialName][2].extend([None] for _ in range(3))
61
                        # UV Coordinates (usually these are not always generated)
62
                        if vtcount != 0:
63
                            self.tmvnig[materialName][1].extend(None for _ in range(3))
64
                            self.fmvnig[materialName][1].extend([None] for _ in range(3))
65
                    elif fval == 'g ':
66
                        # We need this here for any material that is not the last
67
                        # Otherwise it will not know when to stop
68
                        break
69
70
        # Close the file
71
        objfile.close()
72
73
        fcount *= 3
74
        vcount *= 3
75
        vtcount *= 3
76
        vncount *= 3
77
78
        self.tempVertices = [None] * vcount
79
        self.tempNormals = [None] * vncount
80
        self.tempUVs = [None] * fcount
81
82
        self.fnumber = 0
83
        self.vnumber = 0
84
        self.vtnumber = 0
85
        self.vnnumber = 0
86
        self.matnumber = 0
87
88
        # Process the data
89
        self.__process_in_house(__objpath)
90
        # Finalize
91
        self.get_final_data()
92
93
        # Debug Output
94
        #self.write(__txtpath)
95
96
    def write(self, filename):
97
        ''' Used for debuging. '''
98
        objfile = open(filename, "w")
99
        for j in range(self.matnumber):
100
            objfile.write('\n')
101
            matrname = self.tmvnig.keys()[j]
102
            objfile.write(matrname + '\n')
103
            objfile.write('VERTICES\n')
104
            for i in range(len(self.fmvnig[matrname][0]) - 1):
105
                objfile.write(str(self.tmvnig[matrname][0][i]) + " " + str(self.fmvnig[matrname][0][i]) + '\n')
106
            objfile.write('NORMALS\n')
107
            for k in range(len(self.fmvnig[matrname][0]) - 1):
108
                objfile.write(str(self.tmvnig[matrname][2][k]) + " " + str(self.fmvnig[matrname][2][k]) + '\n')
109
        objfile.close()
110
111
    def __process_in_house(self, filename):
112
        matname = None
113
        with open(filename, "r") as objfl:
114
115
            for line in objfl:
116
117
                value = line.split()
118
                valueType = value[0]
119
120
                # Don't bother unless the following key words exist in the line
121
                if valueType not in ['f', 'v', 'vt', 'vn', 'usemtl']:
122
                    continue
123
124
                # Start ignoring the first word of the line to grab the values
125
                value = value[1:]
126
127
                # Check first and continue on early because of string splitting
128
                if valueType == "usemtl":
129
                    matname = value[0]
130
                    # Material Vertex UV Normal Indices Group (Vertex, UV, Normal)
131
                    self.matnumber += 1
132
                    self.fnumber = 0
133
                    continue
134
135
                if valueType == "f":
136
                    temp = [item.split("/") for item in value]
137
138
                    for i in range(3):
139
                        # 0 - Vertex
140
                        # 1 - UV
141
                        # 2 - Normal
142
                        # Make sure UV index data exists
143
                        if temp[i][1] != '':
144
                            self.tmvnig[matname][1][self.fnumber] = int(temp[i][1])
145
                        self.tmvnig[matname][0][self.fnumber] = int(temp[i][0])
146
                        self.tmvnig[matname][2][self.fnumber] = int(temp[i][2])
147
                        self.fnumber += 1
148
                    continue
149
150
                # Map the values after the keyword to floats
151
                value = list(map(float, value))
152
153
                if valueType == "v":
154
                    v = [value[0], value[1], value[2]]
155
                    self.tempVertices[self.vnumber] = v
156
                    self.vnumber += 1
157
158
                elif valueType == "vt":
159
                    vt = [value[0], value[0]]
160
                    self.tempUVs[self.vtnumber] = vt
161
                    self.vtnumber += 1
162
163
                elif valueType == "vn":
164
                    n = [value[0], value[1], value[2]]
165
                    self.tempNormals[self.vnnumber] = n
166
                    self.vnnumber += 1
167
168
169
    def get_final_data(self):
170
        for j in range(self.matnumber):
171
            matrname = self.tmvnig.keys()[j]
172
            for i in range(len(self.tmvnig[matrname][0])):
173
                vertexIndex = int(self.tmvnig[matrname][0][i]) - 1
174
                vertex = self.tempVertices[vertexIndex]
175
                self.fmvnig[matrname][0][i] = vertex
176
177
                normalIndex = int(self.tmvnig[matrname][2][i]) - 1
178
                normal = self.tempNormals[normalIndex]
179
                self.fmvnig[matrname][2][i] = normal
180
181
                if self.uvIndices[0] is None and self.vtnumber != 0:
182
                    uvIndex = int(self.tmvnig[matrname][1][i]) - 1
183
                    uv = self.tempUVs[uvIndex]
184
                    self.fmvnig[matrname][1][i] = uv
185
186
class ObjMesh(MeshBase):
187
    def __init__(self, filePath, name, program, vertexLoc, normalLoc):
188
        super(ObjMesh, self).__init__(filePath, name, program, vertexLoc, normalLoc)
189