1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.resource.obj;
29 
30 import std.stdio;
31 import std.string;
32 import std.format;
33 
34 import dlib.core.memory;
35 import dlib.core.stream;
36 import dlib.math.vector;
37 import dlib.geometry.triangle;
38 import dlib.filesystem.filesystem;
39 import dlib.filesystem.stdfs;
40 import dlib.container.array;
41 
42 import dagon.core.libs;
43 import dagon.core.ownership;
44 import dagon.core.interfaces;
45 import dagon.resource.asset;
46 import dagon.graphics.mesh;
47 
48 import std.regex;
49 
50 struct ObjFace
51 {
52     uint[3] v;
53     uint[3] t;
54     uint[3] n;
55 }
56 
57 class OBJAsset: Asset
58 {
59     Mesh mesh;
60 
61     this(Owner o)
62     {
63         super(o);
64         mesh = New!Mesh(this);
65     }
66 
67     ~this()
68     {
69         release();
70     }
71 
72     override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr)
73     {
74         uint numVerts = 0;
75         uint numNormals = 0;
76         uint numTexcoords = 0;
77         uint numFaces = 0;
78 
79         string fileStr = readText(istrm);
80         foreach(line; lineSplitter(fileStr))
81         {
82             if (line.startsWith("v "))
83                 numVerts++;
84             else if (line.startsWith("vn "))
85                 numNormals++;
86             else if (line.startsWith("vt "))
87                 numTexcoords++;
88             else if (line.startsWith("f "))
89                 numFaces++;
90         }
91         
92         Vector3f[] tmpVertices;
93         Vector3f[] tmpNormals;
94         Vector2f[] tmpTexcoords;
95         ObjFace[] tmpFaces;
96         
97         bool needGenNormals = false;
98         
99         if (!numVerts)
100             writeln("Warning: OBJ file \"", filename, "\" has no vertices");
101         if (!numNormals)
102         {
103             writeln("Warning: OBJ file \"", filename, "\" has no normals (they will be generated)");
104             numNormals = numVerts;
105             needGenNormals = true;
106         }
107         if (!numTexcoords)
108         {
109             writeln("Warning: OBJ file \"", filename, "\" has no texcoords");
110             numTexcoords = numVerts;
111         }
112         
113         if (numVerts)
114             tmpVertices = New!(Vector3f[])(numVerts);
115         if (numNormals)
116             tmpNormals = New!(Vector3f[])(numNormals);
117         if (numTexcoords)
118             tmpTexcoords = New!(Vector2f[])(numTexcoords);
119         if (numFaces)
120             tmpFaces = New!(ObjFace[])(numFaces);
121             
122         tmpVertices[] = Vector3f(0, 0, 0);
123         tmpNormals[] = Vector3f(0, 0, 0);
124         tmpTexcoords[] = Vector2f(0, 0);
125 
126         float x, y, z;
127         int v1, v2, v3, v4;
128         int t1, t2, t3, t4;
129         int n1, n2, n3, n4;
130         uint vi = 0;
131         uint ni = 0;
132         uint ti = 0;
133         uint fi = 0;
134         
135         bool warnAboutQuads = false;
136 
137         foreach(line; lineSplitter(fileStr))
138         {
139             if (line.startsWith("v "))
140             {
141                 if (formattedRead(line, "v %s %s %s", &x, &y, &z))
142                 {
143                     tmpVertices[vi] = Vector3f(x, y, z);
144                     vi++;
145                 }
146             }
147             else if (line.startsWith("vn"))
148             {
149                 if (formattedRead(line, "vn %s %s %s", &x, &y, &z))
150                 {
151                     tmpNormals[ni] = Vector3f(x, y, z);
152                     ni++;
153                 }
154             }
155             else if (line.startsWith("vt"))
156             {
157                 if (formattedRead(line, "vt %s %s", &x, &y))
158                 {
159                     tmpTexcoords[ti] = Vector2f(x, -y);
160                     ti++;
161                 }
162             }
163             else if (line.startsWith("vp"))
164             {
165             }
166             else if (line.startsWith("f"))
167             {
168                 char[256] tmpStr;
169                 tmpStr[0..line.length] = line[];
170                 tmpStr[line.length] = 0;
171             
172                 if (sscanf(tmpStr.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3, &v4, &t4, &n4) == 12)
173                 {
174                     tmpFaces[fi].v[0] = v1-1;
175                     tmpFaces[fi].v[1] = v2-1;
176                     tmpFaces[fi].v[2] = v3-1;
177                     
178                     tmpFaces[fi].t[0] = t1-1;
179                     tmpFaces[fi].t[1] = t2-1;
180                     tmpFaces[fi].t[2] = t3-1;
181                     
182                     tmpFaces[fi].n[0] = n1-1;
183                     tmpFaces[fi].n[1] = n2-1;
184                     tmpFaces[fi].n[2] = n3-1;
185                     
186                     fi++;
187                     
188                     warnAboutQuads = true;
189                 }
190                 if (sscanf(tmpStr.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3) == 9)
191                 {
192                     tmpFaces[fi].v[0] = v1-1;
193                     tmpFaces[fi].v[1] = v2-1;
194                     tmpFaces[fi].v[2] = v3-1;
195                     
196                     tmpFaces[fi].t[0] = t1-1;
197                     tmpFaces[fi].t[1] = t2-1;
198                     tmpFaces[fi].t[2] = t3-1;
199                     
200                     tmpFaces[fi].n[0] = n1-1;
201                     tmpFaces[fi].n[1] = n2-1;
202                     tmpFaces[fi].n[2] = n3-1;
203                     
204                     fi++;
205                 }
206                 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3, &v4, &n4) == 8)
207                 {
208                     tmpFaces[fi].v[0] = v1-1;
209                     tmpFaces[fi].v[1] = v2-1;
210                     tmpFaces[fi].v[2] = v3-1;
211                     
212                     tmpFaces[fi].n[0] = n1-1;
213                     tmpFaces[fi].n[1] = n2-1;
214                     tmpFaces[fi].n[2] = n3-1;
215                     
216                     fi++;
217                     
218                     warnAboutQuads = true;
219                 } 
220                 else if (sscanf(tmpStr.ptr, "f %u/%u %u/%u %u/%u", &v1, &t1, &v2, &t2, &v3, &t3) == 6)
221                 {
222                     tmpFaces[fi].v[0] = v1-1;
223                     tmpFaces[fi].v[1] = v2-1;
224                     tmpFaces[fi].v[2] = v3-1;
225                     
226                     tmpFaces[fi].t[0] = t1-1;
227                     tmpFaces[fi].t[1] = t2-1;
228                     tmpFaces[fi].t[2] = t3-1;
229                     
230                     fi++;
231                 }
232                 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3) == 6)
233                 {
234                     tmpFaces[fi].v[0] = v1-1;
235                     tmpFaces[fi].v[1] = v2-1;
236                     tmpFaces[fi].v[2] = v3-1;
237                     
238                     tmpFaces[fi].n[0] = n1-1;
239                     tmpFaces[fi].n[1] = n2-1;
240                     tmpFaces[fi].n[2] = n3-1;
241                     
242                     fi++;
243                 }
244                 else if (sscanf(tmpStr.ptr, "f %u %u %u %u", &v1, &v2, &v3, &v4) == 4)
245                 {
246                     tmpFaces[fi].v[0] = v1-1;
247                     tmpFaces[fi].v[1] = v2-1;
248                     tmpFaces[fi].v[2] = v3-1;
249                     
250                     fi++;
251                     
252                     warnAboutQuads = true;
253                 }
254                 else if (sscanf(tmpStr.ptr, "f %u %u %u", &v1, &v2, &v3) == 3)
255                 {
256                     tmpFaces[fi].v[0] = v1-1;
257                     tmpFaces[fi].v[1] = v2-1;
258                     tmpFaces[fi].v[2] = v3-1;
259                     
260                     fi++;
261                 }
262                 else
263                     assert(0);
264             }
265         }
266 
267         Delete(fileStr);
268         
269         if (warnAboutQuads)
270             writeln("Warning: OBJ file \"", filename, "\" includes quads, but Dagon supports only triangles");
271         
272         mesh.indices = New!(uint[3][])(tmpFaces.length);
273         uint numUniqueVerts = cast(uint)mesh.indices.length * 3;
274         mesh.vertices = New!(Vector3f[])(numUniqueVerts);
275         mesh.normals = New!(Vector3f[])(numUniqueVerts);
276         mesh.texcoords = New!(Vector2f[])(numUniqueVerts);
277                 
278         uint index = 0;
279         
280         foreach(i, ref ObjFace f; tmpFaces)
281         {
282             if (numVerts)
283             {
284                 mesh.vertices[index] = tmpVertices[f.v[0]];
285                 mesh.vertices[index+1] = tmpVertices[f.v[1]];
286                 mesh.vertices[index+2] = tmpVertices[f.v[2]];
287             }
288             else
289             {
290                 mesh.vertices[index] = Vector3f(0, 0, 0);
291                 mesh.vertices[index+1] = Vector3f(0, 0, 0);
292                 mesh.vertices[index+2] = Vector3f(0, 0, 0);
293             }
294 
295             if (numNormals)
296             {
297                 mesh.normals[index] = tmpNormals[f.n[0]];
298                 mesh.normals[index+1] = tmpNormals[f.n[1]];
299                 mesh.normals[index+2] = tmpNormals[f.n[2]];
300             }
301             else
302             {
303                 mesh.normals[index] = Vector3f(0, 0, 0);
304                 mesh.normals[index+1] = Vector3f(0, 0, 0);
305                 mesh.normals[index+2] = Vector3f(0, 0, 0);
306             }
307             
308             if (numTexcoords)
309             {
310                 mesh.texcoords[index] = tmpTexcoords[f.t[0]];
311                 mesh.texcoords[index+1] = tmpTexcoords[f.t[1]];
312                 mesh.texcoords[index+2] = tmpTexcoords[f.t[2]];
313             }
314             else
315             {
316                 mesh.texcoords[index] = Vector2f(0, 0);
317                 mesh.texcoords[index+1] = Vector2f(0, 0);
318                 mesh.texcoords[index+2] = Vector2f(0, 0);
319             }
320             
321             mesh.indices[i][0] = index;
322             mesh.indices[i][1] = index + 1;
323             mesh.indices[i][2] = index + 2;
324             
325             index += 3;
326         }
327 
328         if (needGenNormals)
329             mesh.generateNormals();
330         
331         if (tmpVertices.length)
332             Delete(tmpVertices);
333         if (tmpNormals.length)
334             Delete(tmpNormals);
335         if (tmpTexcoords.length)
336             Delete(tmpTexcoords);
337         if (tmpFaces.length)
338             Delete(tmpFaces);
339         
340         mesh.dataReady = true;
341 
342         return true;
343     }
344 
345     override bool loadThreadUnsafePart()
346     {
347         mesh.prepareVAO();
348         return true;
349     }
350 
351     override void release()
352     {
353         clearOwnedObjects();
354     }
355 }
356