1 /*
2 Copyright (c) 2014-2015 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module dgl.graphics.mesh;
30 
31 import std.stdio;
32 import std.math;
33 
34 import dlib.core.memory;
35 import dlib.container.array;
36 import dlib.math.vector;
37 import dlib.math.utils;
38 import dlib.geometry.triangle;
39 
40 import derelict.opengl.gl;
41 import derelict.opengl.glext;
42 
43 import dgl.core.interfaces;
44 import dgl.graphics.material;
45 import dgl.graphics.scene;
46 
47 class FaceGroup: Freeable
48 {
49     DynamicArray!Triangle tris;
50     uint displayList;
51     int materialIndex;
52     Material material;
53 
54     void free()
55     {
56         Delete(this);
57     }
58 
59     ~this()
60     {
61         if (glIsList(displayList))
62             glDeleteLists(displayList, 1);
63         tris.free();
64     }
65 }
66 
67 bool vectorsAlmostSame(Vector3f v1, Vector3f v2) nothrow
68 {
69     return (v1 - v2).length < 0.00001f;
70 }
71 
72 int hasVector(ref DynamicArray!Vector3f arr, Vector3f vec)
73 {
74     foreach(i, v; arr.data)
75     {
76         if (vectorsAlmostSame(v, vec))
77             return cast(int)i;
78     }
79     return -1;
80 }
81 
82 bool generateTangentVectors = true;
83 
84 class Mesh: Drawable
85 {
86     int id;
87     string name;
88     Triangle[] tris;
89     DynamicArray!FaceGroup fgroups;
90 
91     this(Triangle[] tris)
92     {
93         this.tris = tris;
94 
95         if (generateTangentVectors)
96             genTangents();
97     }
98 
99     protected void genTangents()
100     {
101         DynamicArray!Vector3f vertices;
102         DynamicArray!Vector3f normals;
103         DynamicArray!Vector2f texcoords;
104         DynamicArray!(uint[3]) triangles;
105 
106         foreach(ref tri; tris)
107         {
108             uint[3] triangle;
109             foreach(i; 0..3)
110             {
111                 Vector3f v = tri.v[i];
112                 Vector3f n = tri.n[i];
113                 Vector2f t = tri.t1[i];
114 
115                 int vi = vertices.hasVector(v);
116 
117                 if (vi == -1)
118                 {
119                     vertices.append(v);
120                     normals.append(n);
121                     texcoords.append(t);
122                     triangle[i] = cast(uint)(vertices.length-1);
123                 }
124                 else
125                 {
126                     triangle[i] = vi;
127                 }
128             }
129             triangles.append(triangle);
130         }
131 
132         Vector3f[] sTan = New!(Vector3f[])(vertices.length);
133         Vector3f[] tTan = New!(Vector3f[])(vertices.length);
134 
135         foreach(i, v; sTan)
136         {
137             sTan[i] = Vector3f(0.0f, 0.0f, 0.0f);
138             tTan[i] = Vector3f(0.0f, 0.0f, 0.0f);
139         }
140 
141         foreach(ref tri; triangles.data)
142         {
143             uint i0 = tri[0];
144             uint i1 = tri[1];
145             uint i2 = tri[2];
146 
147             Vector3f v0 = vertices.data[i0];
148             Vector3f v1 = vertices.data[i1];
149             Vector3f v2 = vertices.data[i2];
150 
151             Vector2f w0 = texcoords.data[i0];
152             Vector2f w1 = texcoords.data[i1];
153             Vector2f w2 = texcoords.data[i2];
154 
155             float x1 = v1.x - v0.x;
156             float x2 = v2.x - v0.x;
157             float y1 = v1.y - v0.y;
158             float y2 = v2.y - v0.y;
159             float z1 = v1.z - v0.z;
160             float z2 = v2.z - v0.z;
161 
162             float s1 = w1[0] - w0[0];
163             float s2 = w2[0] - w0[0];
164             float t1 = w1[1] - w0[1];
165             float t2 = w2[1] - w0[1];
166 
167             float r = (s1 * t2) - (s2 * t1);
168 
169             // Prevent division by zero
170             if (r == 0.0f)
171                 r = 1.0f;
172 
173             float oneOverR = 1.0f / r;
174 
175             Vector3f sDir = Vector3f((t2 * x1 - t1 * x2) * oneOverR,
176                                      (t2 * y1 - t1 * y2) * oneOverR,
177                                      (t2 * z1 - t1 * z2) * oneOverR);
178             Vector3f tDir = Vector3f((s1 * x2 - s2 * x1) * oneOverR,
179                                      (s1 * y2 - s2 * y1) * oneOverR,
180                                      (s1 * z2 - s2 * z1) * oneOverR);
181 
182             sTan[i0] += sDir;
183             tTan[i0] += tDir;
184 
185             sTan[i1] += sDir;
186             tTan[i1] += tDir;
187 
188             sTan[i2] += sDir;
189             tTan[i2] += tDir;
190         }
191 
192         Vector3f[] tangents = New!(Vector3f[])(vertices.length);
193 
194         // Calculate vertex tangent
195         foreach(i, v; tangents)
196         {
197             Vector3f n = normals.data[i];
198             Vector3f t = sTan[i];
199 
200             // Gram-Schmidt orthogonalize
201             tangents[i] = (t - n * dot(n, t));
202             tangents[i].normalize();
203 
204             // Calculate handedness
205             //if (dot(cross(n, t), tTan[i]) < 0.0f)
206 	        //    tangents[i] = -tangents[i];
207         }
208 
209         foreach(ti, ref tri; tris)
210         foreach(i; 0..3)
211         {
212             tri.tg[i] = tangents[triangles.data[ti][i]];
213         }
214 
215         Delete(sTan);
216         Delete(tTan);
217         Delete(tangents);
218         vertices.free();
219         normals.free();
220         texcoords.free();
221         triangles.free();
222     }
223 
224     void genFaceGroups(Scene scene)
225     {
226         // Assign tris to corresponding face groups
227         foreach(tri; tris)
228         {
229             int m = tri.materialIndex;
230             auto fg = getFaceGroupByMaterialId(m);
231             fg.tris.append(tri);
232         }
233 
234         // Assign materials to face groups
235         // and create display lists for them
236         foreach(fg; fgroups.data)
237         {
238             if (fg.materialIndex != -1)
239                 fg.material = scene.getMaterialById(fg.materialIndex);
240             fg.displayList = glGenLists(1);
241             glNewList(fg.displayList, GL_COMPILE);
242             drawTris(fg.tris.data);
243             glEndList();
244         }
245     }
246 
247     protected FaceGroup getFaceGroupByMaterialId(int m)
248     {
249         foreach(i, fg; fgroups.data)
250         {
251             if (fg.materialIndex == m)
252                 return fgroups.data[i];
253         }
254 
255         FaceGroup fg = New!FaceGroup();
256         fg.materialIndex = m;
257         fgroups.append(fg);
258         return fg;
259     }
260 
261     void drawTris(Triangle[] triangles)
262     {
263         glColor4f(1, 1, 1, 1);
264         foreach(tri; triangles)
265         {
266             glBegin(GL_TRIANGLES);
267             // TODO: add possibility to select between
268             // per face normals and per vertex normals
269             //glNormal3fv(tri.normal.arrayof.ptr);
270 
271             glNormal3fv(tri.n[0].arrayof.ptr);
272             if (generateTangentVectors)
273                 glColor3fv(tri.tg[0].arrayof.ptr);
274             glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, tri.t1[0].arrayof.ptr);
275             glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, tri.t2[0].arrayof.ptr);
276             glVertex3fv(tri.v[0].arrayof.ptr);
277 
278             glNormal3fv(tri.n[1].arrayof.ptr);
279             if (generateTangentVectors)
280                 glColor3fv(tri.tg[1].arrayof.ptr);
281             glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, tri.t1[1].arrayof.ptr);
282             glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, tri.t2[1].arrayof.ptr);
283             glVertex3fv(tri.v[1].arrayof.ptr);
284 
285             glNormal3fv(tri.n[2].arrayof.ptr);
286             if (generateTangentVectors)
287                 glColor3fv(tri.tg[2].arrayof.ptr);
288             glMultiTexCoord2fvARB(GL_TEXTURE0_ARB, tri.t1[2].arrayof.ptr);
289             glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, tri.t2[2].arrayof.ptr);
290             glVertex3fv(tri.v[2].arrayof.ptr);
291             glEnd();
292         }
293     }
294 
295     override void draw(double dt)
296     {
297         foreach(fg; fgroups.data)
298         {
299             if (fg.material)
300                 fg.material.bind(dt);
301 
302             if (glIsList(fg.displayList))
303                 glCallList(fg.displayList);
304 
305             if (fg.material)
306                 fg.material.unbind();
307         }
308     }
309 
310     void freeContent()
311     {
312         if (name.length)
313             Delete(name);
314         Delete(tris);
315         foreach(fg; fgroups.data)
316             fg.free();
317         fgroups.free();
318     }
319 
320     void free()
321     {
322         Delete(this);
323     }
324 
325     ~this()
326     {
327         freeContent();
328     }
329 }