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.graphics.shapes; 29 30 import std.math; 31 32 import dlib.core.memory; 33 import dlib.core.ownership; 34 import dlib.math.vector; 35 import dlib.container.array; 36 37 import dagon.core.bindings; 38 import dagon.graphics.drawable; 39 import dagon.graphics.mesh; 40 41 class ShapePlane: Mesh 42 { 43 this(float sx, float sz, uint numTiles, Owner owner) 44 { 45 super(owner); 46 47 float px = -sx * 0.5f; 48 float py = -sz * 0.5f; 49 50 float tileWidth = sx / numTiles; 51 float tileHeight = sz / numTiles; 52 53 Vector3f start = Vector3f(px, 0.0f, py); 54 55 uint gridSize = numTiles + 1; 56 57 vertices = New!(Vector3f[])(gridSize * gridSize); 58 normals = New!(Vector3f[])(gridSize * gridSize); 59 texcoords = New!(Vector2f[])(gridSize * gridSize); 60 61 for (uint i = 0, y = 0; y < gridSize; y++) 62 for (uint x = 0; x < gridSize; x++, i++) 63 { 64 vertices[i] = start + Vector3f(x * tileWidth, 0, y * tileHeight); 65 normals[i] = Vector3f(0, 1, 0); 66 texcoords[i] = Vector2f(x, y); 67 } 68 69 indices = New!(uint[3][])(gridSize * gridSize * 2); 70 71 uint index = 0; 72 for (uint y = 0; y < gridSize - 1; y++) 73 for (uint x = 0; x < gridSize - 1; x++) 74 { 75 uint offset = y * gridSize + x; 76 indices[index][2] = (offset + 0); 77 indices[index][1] = (offset + 1); 78 indices[index][0] = (offset + gridSize); 79 80 indices[index+1][2] = (offset + 1); 81 indices[index+1][1] = (offset + gridSize + 1); 82 indices[index+1][0] = (offset + gridSize); 83 84 index += 2; 85 } 86 87 dataReady = true; 88 prepareVAO(); 89 } 90 } 91 92 class ShapeQuad: Owner, Drawable 93 { 94 Vector2f[4] vertices; 95 Vector2f[4] texcoords; 96 uint[3][2] indices; 97 98 GLuint vao = 0; 99 GLuint vbo = 0; 100 GLuint tbo = 0; 101 GLuint eao = 0; 102 103 this(Owner owner) 104 { 105 super(owner); 106 107 vertices[0] = Vector2f(0, 1); 108 vertices[1] = Vector2f(0, 0); 109 vertices[2] = Vector2f(1, 0); 110 vertices[3] = Vector2f(1, 1); 111 112 texcoords[0] = Vector2f(0, 1); 113 texcoords[1] = Vector2f(0, 0); 114 texcoords[2] = Vector2f(1, 0); 115 texcoords[3] = Vector2f(1, 1); 116 117 indices[0][0] = 0; 118 indices[0][1] = 1; 119 indices[0][2] = 2; 120 121 indices[1][0] = 0; 122 indices[1][1] = 2; 123 indices[1][2] = 3; 124 125 glGenBuffers(1, &vbo); 126 glBindBuffer(GL_ARRAY_BUFFER, vbo); 127 glBufferData(GL_ARRAY_BUFFER, vertices.length * float.sizeof * 2, vertices.ptr, GL_STATIC_DRAW); 128 129 glGenBuffers(1, &tbo); 130 glBindBuffer(GL_ARRAY_BUFFER, tbo); 131 glBufferData(GL_ARRAY_BUFFER, texcoords.length * float.sizeof * 2, texcoords.ptr, GL_STATIC_DRAW); 132 133 glGenBuffers(1, &eao); 134 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 135 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof * 3, indices.ptr, GL_STATIC_DRAW); 136 137 glGenVertexArrays(1, &vao); 138 glBindVertexArray(vao); 139 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 140 141 glEnableVertexAttribArray(VertexAttrib.Vertices); 142 glBindBuffer(GL_ARRAY_BUFFER, vbo); 143 glVertexAttribPointer(VertexAttrib.Vertices, 2, GL_FLOAT, GL_FALSE, 0, null); 144 145 glEnableVertexAttribArray(VertexAttrib.Texcoords); 146 glBindBuffer(GL_ARRAY_BUFFER, tbo); 147 glVertexAttribPointer(VertexAttrib.Texcoords, 2, GL_FLOAT, GL_FALSE, 0, null); 148 149 glBindVertexArray(0); 150 } 151 152 ~this() 153 { 154 glDeleteVertexArrays(1, &vao); 155 glDeleteBuffers(1, &vbo); 156 glDeleteBuffers(1, &tbo); 157 glDeleteBuffers(1, &eao); 158 } 159 160 void render(GraphicsState* state) 161 { 162 glDepthMask(0); 163 glBindVertexArray(vao); 164 glDrawElements(GL_TRIANGLES, cast(uint)indices.length * 3, GL_UNSIGNED_INT, cast(void*)0); 165 glBindVertexArray(0); 166 glDepthMask(1); 167 } 168 } 169 170 class ShapeBox: Mesh 171 { 172 this(Vector3f extents, Owner owner) 173 { 174 super(owner); 175 176 vertices = New!(Vector3f[])(24); 177 normals = New!(Vector3f[])(24); 178 texcoords = New!(Vector2f[])(24); 179 indices = New!(uint[3][])(12); 180 181 Vector3f pmax = +extents; 182 Vector3f pmin = -extents; 183 184 texcoords[0] = Vector2f(1, 0); normals[0] = Vector3f(0,0,1); vertices[0] = Vector3f(pmax.x, pmax.y, pmax.z); 185 texcoords[1] = Vector2f(0, 0); normals[1] = Vector3f(0,0,1); vertices[1] = Vector3f(pmin.x, pmax.y, pmax.z); 186 texcoords[2] = Vector2f(0, 1); normals[2] = Vector3f(0,0,1); vertices[2] = Vector3f(pmin.x, pmin.y, pmax.z); 187 texcoords[3] = Vector2f(1, 1); normals[3] = Vector3f(0,0,1); vertices[3] = Vector3f(pmax.x, pmin.y, pmax.z); 188 indices[0][0] = 0; indices[0][1] = 1; indices[0][2] = 2; 189 indices[1][0] = 2; indices[1][1] = 3; indices[1][2] = 0; 190 191 texcoords[4] = Vector2f(0, 0); normals[4] = Vector3f(1,0,0); vertices[4] = Vector3f(pmax.x, pmax.y, pmax.z); 192 texcoords[5] = Vector2f(0, 1); normals[5] = Vector3f(1,0,0); vertices[5] = Vector3f(pmax.x, pmin.y, pmax.z); 193 texcoords[6] = Vector2f(1, 1); normals[6] = Vector3f(1,0,0); vertices[6] = Vector3f(pmax.x, pmin.y, pmin.z); 194 texcoords[7] = Vector2f(1, 0); normals[7] = Vector3f(1,0,0); vertices[7] = Vector3f(pmax.x, pmax.y, pmin.z); 195 indices[2][0] = 4; indices[2][1] = 5; indices[2][2] = 6; 196 indices[3][0] = 6; indices[3][1] = 7; indices[3][2] = 4; 197 198 texcoords[8] = Vector2f(1, 1); normals[8] = Vector3f(0,1,0); vertices[8] = Vector3f(pmax.x, pmax.y, pmax.z); 199 texcoords[9] = Vector2f(1, 0); normals[9] = Vector3f(0,1,0); vertices[9] = Vector3f(pmax.x, pmax.y, pmin.z); 200 texcoords[10] = Vector2f(0, 0); normals[10] = Vector3f(0,1,0); vertices[10] = Vector3f(pmin.x, pmax.y, pmin.z); 201 texcoords[11] = Vector2f(0, 1); normals[11] = Vector3f(0,1,0); vertices[11] = Vector3f(pmin.x, pmax.y, pmax.z); 202 indices[4][0] = 8; indices[4][1] = 9; indices[4][2] = 10; 203 indices[5][0] = 10; indices[5][1] = 11; indices[5][2] = 8; 204 205 texcoords[12] = Vector2f(1, 0); normals[12] = Vector3f(-1,0,0); vertices[12] = Vector3f(pmin.x, pmax.y, pmax.z); 206 texcoords[13] = Vector2f(0, 0); normals[13] = Vector3f(-1,0,0); vertices[13] = Vector3f(pmin.x, pmax.y, pmin.z); 207 texcoords[14] = Vector2f(0, 1); normals[14] = Vector3f(-1,0,0); vertices[14] = Vector3f(pmin.x, pmin.y, pmin.z); 208 texcoords[15] = Vector2f(1, 1); normals[15] = Vector3f(-1,0,0); vertices[15] = Vector3f(pmin.x, pmin.y, pmax.z); 209 indices[6][0] = 12; indices[6][1] = 13; indices[6][2] = 14; 210 indices[7][0] = 14; indices[7][1] = 15; indices[7][2] = 12; 211 212 texcoords[16] = Vector2f(0, 1); normals[16] = Vector3f(0,-1,0); vertices[16] = Vector3f(pmin.x, pmin.y, pmin.z); 213 texcoords[17] = Vector2f(1, 1); normals[17] = Vector3f(0,-1,0); vertices[17] = Vector3f(pmax.x, pmin.y, pmin.z); 214 texcoords[18] = Vector2f(1, 0); normals[18] = Vector3f(0,-1,0); vertices[18] = Vector3f(pmax.x, pmin.y, pmax.z); 215 texcoords[19] = Vector2f(0, 0); normals[19] = Vector3f(0,-1,0); vertices[19] = Vector3f(pmin.x, pmin.y, pmax.z); 216 indices[8][0] = 16; indices[8][1] = 17; indices[8][2] = 18; 217 indices[9][0] = 18; indices[9][1] = 19; indices[9][2] = 16; 218 219 texcoords[20] = Vector2f(0, 1); normals[20] = Vector3f(0,0,-1); vertices[20] = Vector3f(pmax.x, pmin.y, pmin.z); 220 texcoords[21] = Vector2f(1, 1); normals[21] = Vector3f(0,0,-1); vertices[21] = Vector3f(pmin.x, pmin.y, pmin.z); 221 texcoords[22] = Vector2f(1, 0); normals[22] = Vector3f(0,0,-1); vertices[22] = Vector3f(pmin.x, pmax.y, pmin.z); 222 texcoords[23] = Vector2f(0, 0); normals[23] = Vector3f(0,0,-1); vertices[23] = Vector3f(pmax.x, pmax.y, pmin.z); 223 indices[10][0] = 20; indices[10][1] = 21; indices[10][2] = 22; 224 indices[11][0] = 22; indices[11][1] = 23; indices[11][2] = 20; 225 226 dataReady = true; 227 prepareVAO(); 228 } 229 } 230 231 enum PI2 = PI * 2.0f; 232 enum HALF_PI = PI * 0.5f; 233 234 Vector2f envMapEquirect(Vector3f dir) 235 { 236 Vector2f uv; 237 uv.y = acos(dir.y) / PI; 238 uv.x = (PI + atan2(dir.x, dir.z)) / PI2; 239 return uv; 240 } 241 242 class ShapeSphere: Mesh 243 { 244 Array!Vector3f daVertices; 245 Array!Vector3f daNormals; 246 Array!Vector2f daTexcoords; 247 Array!(uint[3]) daIndices; 248 249 this(float radius, int slices, int stacks, bool invNormals, Owner owner) 250 { 251 super(owner); 252 253 float X1, Y1, X2, Y2, Z1, Z2; 254 float inc1, inc2, inc3, inc4, inc5, radius1, radius2; 255 uint[3] tri; 256 uint i = 0; 257 258 float cuts = stacks; 259 float invCuts = 1.0f / cuts; 260 float heightStep = 2.0f * invCuts; 261 262 float invSlices = 1.0f / slices; 263 float angleStep = (2.0f * PI) * invSlices; 264 265 for(int h = 0; h < stacks; h++) 266 { 267 float h1Norm = cast(float)h * invCuts * 2.0f - 1.0f; 268 float h2Norm = cast(float)(h+1) * invCuts * 2.0f - 1.0f; 269 float y1 = sin(HALF_PI * h1Norm); 270 float y2 = sin(HALF_PI * h2Norm); 271 272 float circleRadius1 = cos(HALF_PI * y1); 273 float circleRadius2 = cos(HALF_PI * y2); 274 275 for(int a = 0; a < slices; a++) 276 { 277 float x1a = sin(angleStep * a) * circleRadius1; 278 float z1a = cos(angleStep * a) * circleRadius1; 279 float x2a = sin(angleStep * (a + 1)) * circleRadius1; 280 float z2a = cos(angleStep * (a + 1)) * circleRadius1; 281 282 float x1b = sin(angleStep * a) * circleRadius2; 283 float z1b = cos(angleStep * a) * circleRadius2; 284 float x2b = sin(angleStep * (a + 1)) * circleRadius2; 285 float z2b = cos(angleStep * (a + 1)) * circleRadius2; 286 287 Vector3f v1 = Vector3f(x1a, y1, z1a); 288 Vector3f v2 = Vector3f(x2a, y1, z2a); 289 Vector3f v3 = Vector3f(x1b, y2, z1b); 290 Vector3f v4 = Vector3f(x2b, y2, z2b); 291 292 Vector3f n1 = v1.normalized; 293 Vector3f n2 = v2.normalized; 294 Vector3f n3 = v3.normalized; 295 Vector3f n4 = v4.normalized; 296 297 daVertices.append(n1 * radius); 298 daVertices.append(n2 * radius); 299 daVertices.append(n3 * radius); 300 301 daVertices.append(n3 * radius); 302 daVertices.append(n2 * radius); 303 daVertices.append(n4 * radius); 304 305 float sign = invNormals? -1.0f : 1.0f; 306 307 daNormals.append(n1 * sign); 308 daNormals.append(n2 * sign); 309 daNormals.append(n3 * sign); 310 311 daNormals.append(n3 * sign); 312 daNormals.append(n2 * sign); 313 daNormals.append(n4 * sign); 314 315 auto uv1 = Vector2f(0, 1); 316 auto uv2 = Vector2f(1, 1); 317 auto uv3 = Vector2f(0, 0); 318 auto uv4 = Vector2f(1, 0); 319 320 daTexcoords.append(uv1); 321 daTexcoords.append(uv2); 322 daTexcoords.append(uv3); 323 324 daTexcoords.append(uv3); 325 daTexcoords.append(uv2); 326 daTexcoords.append(uv4); 327 328 if (invNormals) 329 { 330 tri[0] = i+2; 331 tri[1] = i+1; 332 tri[2] = i; 333 daIndices.append(tri); 334 335 tri[0] = i+5; 336 tri[1] = i+4; 337 tri[2] = i+3; 338 daIndices.append(tri); 339 } 340 else 341 { 342 tri[0] = i; 343 tri[1] = i+1; 344 tri[2] = i+2; 345 daIndices.append(tri); 346 347 tri[0] = i+3; 348 tri[1] = i+4; 349 tri[2] = i+5; 350 daIndices.append(tri); 351 } 352 353 i += 6; 354 } 355 } 356 357 vertices = New!(Vector3f[])(daVertices.length); 358 vertices[] = daVertices.data[]; 359 360 normals = New!(Vector3f[])(daNormals.length); 361 normals[] = daNormals.data[]; 362 363 texcoords = New!(Vector2f[])(daTexcoords.length); 364 texcoords[] = daTexcoords.data[]; 365 366 indices = New!(uint[3][])(daIndices.length); 367 indices[] = daIndices.data[]; 368 369 daVertices.free(); 370 daNormals.free(); 371 daTexcoords.free(); 372 daIndices.free(); 373 374 dataReady = true; 375 prepareVAO(); 376 } 377 } 378 379 class ShapeDisk: Mesh 380 { 381 this(float radius, uint slices, Owner owner) 382 { 383 super(owner); 384 385 vertices = New!(Vector3f[])(slices + 1); 386 normals = New!(Vector3f[])(slices + 1); 387 texcoords = New!(Vector2f[])(slices + 1); 388 indices = New!(uint[3][])(slices); 389 390 float angleStep = (2.0f * PI) / slices; 391 float angle = 0.0f; 392 uint vi = 0; 393 uint i = 0; 394 395 vertices[vi] = Vector3f(0.0f, 0.0f, 0.0f); 396 normals[vi] = Vector3f(0.0f, 1.0f, 0.0f); 397 texcoords[vi] = Vector2f(0.5f, 0.5f); 398 vi++; 399 400 for(uint s = 0; s < slices; s++) 401 { 402 float x = cos(angle); 403 float z = sin(angle); 404 405 vertices[vi] = Vector3f(x, 0.0f, z) * radius; 406 normals[vi] = Vector3f(0.0f, 1.0f, 0.0f); 407 texcoords[vi] = Vector2f(x * 0.5f + 0.5f, z * 0.5f + 0.5f); 408 409 indices[i][0] = 0; 410 if (s < slices - 1) 411 indices[i][1] = vi + 1; 412 else 413 indices[i][1] = indices[0][2]; 414 indices[i][2] = vi; 415 416 vi++; 417 i++; 418 419 angle += angleStep; 420 } 421 422 dataReady = true; 423 prepareVAO(); 424 } 425 } 426 427 // TODO: other shapes from original Dagon