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 }