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