1 /*
2 Copyright (c) 2017-2019 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 //import std.regex;
34 
35 import dlib.core.memory;
36 import dlib.core.ownership;
37 import dlib.core.stream;
38 import dlib.math.vector;
39 import dlib.geometry.triangle;
40 import dlib.filesystem.filesystem;
41 import dlib.filesystem.stdfs;
42 import dlib.container.array;
43 
44 import dagon.core.bindings;
45 import dagon.resource.asset;
46 import dagon.graphics.mesh;
47 
48 struct ObjFace
49 {
50     uint[3] v;
51     uint[3] t;
52     uint[3] n;
53 }
54 
55 class OBJAsset: Asset
56 {
57     Mesh mesh;
58 
59     this(Owner o)
60     {
61         super(o);
62         mesh = New!Mesh(this);
63     }
64 
65     ~this()
66     {
67         release();
68     }
69 
70     override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr)
71     {
72         uint numVerts = 0;
73         uint numNormals = 0;
74         uint numTexcoords = 0;
75         uint numFaces = 0;
76 
77         string fileStr = readText(istrm);
78         foreach(line; lineSplitter(fileStr))
79         {
80             if (line.startsWith("v "))
81                 numVerts++;
82             else if (line.startsWith("vn "))
83                 numNormals++;
84             else if (line.startsWith("vt "))
85                 numTexcoords++;
86             else if (line.startsWith("f "))
87                 numFaces++;
88         }
89         
90         Vector3f[] tmpVertices;
91         Vector3f[] tmpNormals;
92         Vector2f[] tmpTexcoords;
93         ObjFace[] tmpFaces;
94         
95         bool needGenNormals = false;
96         
97         if (!numVerts)
98             writeln("Warning: OBJ file \"", filename, "\" has no vertices");
99         if (!numNormals)
100         {
101             writeln("Warning: OBJ file \"", filename, "\" has no normals (they will be generated)");
102             numNormals = numVerts;
103             needGenNormals = true;
104         }
105         if (!numTexcoords)
106         {
107             writeln("Warning: OBJ file \"", filename, "\" has no texcoords");
108             numTexcoords = numVerts;
109         }
110         
111         if (numVerts)
112             tmpVertices = New!(Vector3f[])(numVerts);
113         if (numNormals)
114             tmpNormals = New!(Vector3f[])(numNormals);
115         if (numTexcoords)
116             tmpTexcoords = New!(Vector2f[])(numTexcoords);
117         if (numFaces)
118             tmpFaces = New!(ObjFace[])(numFaces);
119             
120         tmpVertices[] = Vector3f(0, 0, 0);
121         tmpNormals[] = Vector3f(0, 0, 0);
122         tmpTexcoords[] = Vector2f(0, 0);
123 
124         float x, y, z;
125         uint v1, v2, v3, v4;
126         uint t1, t2, t3, t4;
127         uint n1, n2, n3, n4;
128         uint vi = 0;
129         uint ni = 0;
130         uint ti = 0;
131         uint fi = 0;
132         
133         bool warnAboutQuads = false;
134 
135         foreach(line; lineSplitter(fileStr))
136         {
137             if (line.startsWith("v "))
138             {
139                 if (formattedRead(line, "v %s %s %s", &x, &y, &z))
140                 {
141                     tmpVertices[vi] = Vector3f(x, y, z);
142                     vi++;
143                 }
144             }
145             else if (line.startsWith("vn"))
146             {
147                 if (formattedRead(line, "vn %s %s %s", &x, &y, &z))
148                 {
149                     tmpNormals[ni] = Vector3f(x, y, z);
150                     ni++;
151                 }
152             }
153             else if (line.startsWith("vt"))
154             {
155                 if (formattedRead(line, "vt %s %s", &x, &y))
156                 {
157                     tmpTexcoords[ti] = Vector2f(x, -y);
158                     ti++;
159                 }
160             }
161             else if (line.startsWith("vp"))
162             {
163             }
164             else if (line.startsWith("f"))
165             {
166                 char[256] tmpStr;
167                 tmpStr[0..line.length] = line[];
168                 tmpStr[line.length] = 0;
169             
170                 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)
171                 {
172                     tmpFaces[fi].v[0] = v1-1;
173                     tmpFaces[fi].v[1] = v2-1;
174                     tmpFaces[fi].v[2] = v3-1;
175                     
176                     tmpFaces[fi].t[0] = t1-1;
177                     tmpFaces[fi].t[1] = t2-1;
178                     tmpFaces[fi].t[2] = t3-1;
179                     
180                     tmpFaces[fi].n[0] = n1-1;
181                     tmpFaces[fi].n[1] = n2-1;
182                     tmpFaces[fi].n[2] = n3-1;
183                     
184                     fi++;
185                     
186                     warnAboutQuads = true;
187                 }
188                 if (sscanf(tmpStr.ptr, "f %u/%u/%u %u/%u/%u %u/%u/%u", &v1, &t1, &n1, &v2, &t2, &n2, &v3, &t3, &n3) == 9)
189                 {
190                     tmpFaces[fi].v[0] = v1-1;
191                     tmpFaces[fi].v[1] = v2-1;
192                     tmpFaces[fi].v[2] = v3-1;
193                     
194                     tmpFaces[fi].t[0] = t1-1;
195                     tmpFaces[fi].t[1] = t2-1;
196                     tmpFaces[fi].t[2] = t3-1;
197                     
198                     tmpFaces[fi].n[0] = n1-1;
199                     tmpFaces[fi].n[1] = n2-1;
200                     tmpFaces[fi].n[2] = n3-1;
201                     
202                     fi++;
203                 }
204                 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3, &v4, &n4) == 8)
205                 {
206                     tmpFaces[fi].v[0] = v1-1;
207                     tmpFaces[fi].v[1] = v2-1;
208                     tmpFaces[fi].v[2] = v3-1;
209                     
210                     tmpFaces[fi].n[0] = n1-1;
211                     tmpFaces[fi].n[1] = n2-1;
212                     tmpFaces[fi].n[2] = n3-1;
213                     
214                     fi++;
215                     
216                     warnAboutQuads = true;
217                 } 
218                 else if (sscanf(tmpStr.ptr, "f %u/%u %u/%u %u/%u", &v1, &t1, &v2, &t2, &v3, &t3) == 6)
219                 {
220                     tmpFaces[fi].v[0] = v1-1;
221                     tmpFaces[fi].v[1] = v2-1;
222                     tmpFaces[fi].v[2] = v3-1;
223                     
224                     tmpFaces[fi].t[0] = t1-1;
225                     tmpFaces[fi].t[1] = t2-1;
226                     tmpFaces[fi].t[2] = t3-1;
227                     
228                     fi++;
229                 }
230                 else if (sscanf(tmpStr.ptr, "f %u//%u %u//%u %u//%u", &v1, &n1, &v2, &n2, &v3, &n3) == 6)
231                 {
232                     tmpFaces[fi].v[0] = v1-1;
233                     tmpFaces[fi].v[1] = v2-1;
234                     tmpFaces[fi].v[2] = v3-1;
235                     
236                     tmpFaces[fi].n[0] = n1-1;
237                     tmpFaces[fi].n[1] = n2-1;
238                     tmpFaces[fi].n[2] = n3-1;
239                     
240                     fi++;
241                 }
242                 else if (sscanf(tmpStr.ptr, "f %u %u %u %u", &v1, &v2, &v3, &v4) == 4)
243                 {
244                     tmpFaces[fi].v[0] = v1-1;
245                     tmpFaces[fi].v[1] = v2-1;
246                     tmpFaces[fi].v[2] = v3-1;
247                     
248                     fi++;
249                     
250                     warnAboutQuads = true;
251                 }
252                 else if (sscanf(tmpStr.ptr, "f %u %u %u", &v1, &v2, &v3) == 3)
253                 {
254                     tmpFaces[fi].v[0] = v1-1;
255                     tmpFaces[fi].v[1] = v2-1;
256                     tmpFaces[fi].v[2] = v3-1;
257                     
258                     fi++;
259                 }
260                 else
261                     assert(0);
262             }
263         }
264 
265         Delete(fileStr);
266         
267         if (warnAboutQuads)
268             writeln("Warning: OBJ file \"", filename, "\" includes quads, but Dagon supports only triangles");
269         
270         mesh.indices = New!(uint[3][])(tmpFaces.length);
271         uint numUniqueVerts = cast(uint)mesh.indices.length * 3;
272         mesh.vertices = New!(Vector3f[])(numUniqueVerts);
273         mesh.normals = New!(Vector3f[])(numUniqueVerts);
274         mesh.texcoords = New!(Vector2f[])(numUniqueVerts);
275                 
276         uint index = 0;
277         
278         foreach(i, ref ObjFace f; tmpFaces)
279         {
280             if (numVerts)
281             {
282                 mesh.vertices[index] = tmpVertices[f.v[0]];
283                 mesh.vertices[index+1] = tmpVertices[f.v[1]];
284                 mesh.vertices[index+2] = tmpVertices[f.v[2]];
285             }
286             else
287             {
288                 mesh.vertices[index] = Vector3f(0, 0, 0);
289                 mesh.vertices[index+1] = Vector3f(0, 0, 0);
290                 mesh.vertices[index+2] = Vector3f(0, 0, 0);
291             }
292 
293             if (numNormals)
294             {
295                 mesh.normals[index] = tmpNormals[f.n[0]];
296                 mesh.normals[index+1] = tmpNormals[f.n[1]];
297                 mesh.normals[index+2] = tmpNormals[f.n[2]];
298             }
299             else
300             {
301                 mesh.normals[index] = Vector3f(0, 0, 0);
302                 mesh.normals[index+1] = Vector3f(0, 0, 0);
303                 mesh.normals[index+2] = Vector3f(0, 0, 0);
304             }
305             
306             if (numTexcoords)
307             {
308                 mesh.texcoords[index] = tmpTexcoords[f.t[0]];
309                 mesh.texcoords[index+1] = tmpTexcoords[f.t[1]];
310                 mesh.texcoords[index+2] = tmpTexcoords[f.t[2]];
311             }
312             else
313             {
314                 mesh.texcoords[index] = Vector2f(0, 0);
315                 mesh.texcoords[index+1] = Vector2f(0, 0);
316                 mesh.texcoords[index+2] = Vector2f(0, 0);
317             }
318             
319             mesh.indices[i][0] = index;
320             mesh.indices[i][1] = index + 1;
321             mesh.indices[i][2] = index + 2;
322             
323             index += 3;
324         }
325 
326         if (needGenNormals)
327             mesh.generateNormals();
328         
329         if (tmpVertices.length)
330             Delete(tmpVertices);
331         if (tmpNormals.length)
332             Delete(tmpNormals);
333         if (tmpTexcoords.length)
334             Delete(tmpTexcoords);
335         if (tmpFaces.length)
336             Delete(tmpFaces);
337         
338         mesh.calcBoundingBox();
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