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