1 /* 2 Copyright (c) 2017-2022 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 this(float hw, float hh, float hd, Owner owner) 231 { 232 this(Vector3f(hw, hh, hd), owner); 233 } 234 } 235 236 enum PI2 = PI * 2.0f; 237 enum HALF_PI = PI * 0.5f; 238 239 Vector2f envMapEquirect(Vector3f dir) 240 { 241 Vector2f uv; 242 uv.y = acos(dir.y) / PI; 243 uv.x = (PI + atan2(dir.x, dir.z)) / PI2; 244 return uv; 245 } 246 247 class ShapeSphere: Mesh 248 { 249 Array!Vector3f daVertices; 250 Array!Vector3f daNormals; 251 Array!Vector2f daTexcoords; 252 Array!(uint[3]) daIndices; 253 254 this(float radius, int slices, int stacks, bool invNormals, Owner owner) 255 { 256 super(owner); 257 258 float X1, Y1, X2, Y2, Z1, Z2; 259 float inc1, inc2, inc3, inc4, inc5, radius1, radius2; 260 uint[3] tri; 261 uint i = 0; 262 263 float cuts = stacks; 264 float invCuts = 1.0f / cuts; 265 float heightStep = 2.0f * invCuts; 266 267 float invSlices = 1.0f / slices; 268 float angleStep = (2.0f * PI) * invSlices; 269 270 for(int h = 0; h < stacks; h++) 271 { 272 float h1Norm = cast(float)h * invCuts * 2.0f - 1.0f; 273 float h2Norm = cast(float)(h+1) * invCuts * 2.0f - 1.0f; 274 float y1 = sin(HALF_PI * h1Norm); 275 float y2 = sin(HALF_PI * h2Norm); 276 277 float circleRadius1 = cos(HALF_PI * y1); 278 float circleRadius2 = cos(HALF_PI * y2); 279 280 for(int a = 0; a < slices; a++) 281 { 282 float x1a = sin(angleStep * a) * circleRadius1; 283 float z1a = cos(angleStep * a) * circleRadius1; 284 float x2a = sin(angleStep * (a + 1)) * circleRadius1; 285 float z2a = cos(angleStep * (a + 1)) * circleRadius1; 286 287 float x1b = sin(angleStep * a) * circleRadius2; 288 float z1b = cos(angleStep * a) * circleRadius2; 289 float x2b = sin(angleStep * (a + 1)) * circleRadius2; 290 float z2b = cos(angleStep * (a + 1)) * circleRadius2; 291 292 Vector3f v1 = Vector3f(x1a, y1, z1a); 293 Vector3f v2 = Vector3f(x2a, y1, z2a); 294 Vector3f v3 = Vector3f(x1b, y2, z1b); 295 Vector3f v4 = Vector3f(x2b, y2, z2b); 296 297 Vector3f n1 = v1.normalized; 298 Vector3f n2 = v2.normalized; 299 Vector3f n3 = v3.normalized; 300 Vector3f n4 = v4.normalized; 301 302 daVertices.append(n1 * radius); 303 daVertices.append(n2 * radius); 304 daVertices.append(n3 * radius); 305 306 daVertices.append(n3 * radius); 307 daVertices.append(n2 * radius); 308 daVertices.append(n4 * radius); 309 310 float sign = invNormals? -1.0f : 1.0f; 311 312 daNormals.append(n1 * sign); 313 daNormals.append(n2 * sign); 314 daNormals.append(n3 * sign); 315 316 daNormals.append(n3 * sign); 317 daNormals.append(n2 * sign); 318 daNormals.append(n4 * sign); 319 320 auto uv1 = Vector2f(0, 1); 321 auto uv2 = Vector2f(1, 1); 322 auto uv3 = Vector2f(0, 0); 323 auto uv4 = Vector2f(1, 0); 324 325 daTexcoords.append(uv1); 326 daTexcoords.append(uv2); 327 daTexcoords.append(uv3); 328 329 daTexcoords.append(uv3); 330 daTexcoords.append(uv2); 331 daTexcoords.append(uv4); 332 333 if (invNormals) 334 { 335 tri[0] = i+2; 336 tri[1] = i+1; 337 tri[2] = i; 338 daIndices.append(tri); 339 340 tri[0] = i+5; 341 tri[1] = i+4; 342 tri[2] = i+3; 343 daIndices.append(tri); 344 } 345 else 346 { 347 tri[0] = i; 348 tri[1] = i+1; 349 tri[2] = i+2; 350 daIndices.append(tri); 351 352 tri[0] = i+3; 353 tri[1] = i+4; 354 tri[2] = i+5; 355 daIndices.append(tri); 356 } 357 358 i += 6; 359 } 360 } 361 362 vertices = New!(Vector3f[])(daVertices.length); 363 vertices[] = daVertices.data[]; 364 365 normals = New!(Vector3f[])(daNormals.length); 366 normals[] = daNormals.data[]; 367 368 texcoords = New!(Vector2f[])(daTexcoords.length); 369 texcoords[] = daTexcoords.data[]; 370 371 indices = New!(uint[3][])(daIndices.length); 372 indices[] = daIndices.data[]; 373 374 daVertices.free(); 375 daNormals.free(); 376 daTexcoords.free(); 377 daIndices.free(); 378 379 dataReady = true; 380 prepareVAO(); 381 } 382 } 383 384 class ShapeDisk: Mesh 385 { 386 this(float radius, uint slices, Owner owner) 387 { 388 super(owner); 389 390 vertices = New!(Vector3f[])(slices + 1); 391 normals = New!(Vector3f[])(slices + 1); 392 texcoords = New!(Vector2f[])(slices + 1); 393 indices = New!(uint[3][])(slices); 394 395 float angleStep = (2.0f * PI) / slices; 396 float angle = 0.0f; 397 uint vi = 0; 398 uint i = 0; 399 400 vertices[vi] = Vector3f(0.0f, 0.0f, 0.0f); 401 normals[vi] = Vector3f(0.0f, 1.0f, 0.0f); 402 texcoords[vi] = Vector2f(0.5f, 0.5f); 403 vi++; 404 405 for(uint s = 0; s < slices; s++) 406 { 407 float x = cos(angle); 408 float z = sin(angle); 409 410 vertices[vi] = Vector3f(x, 0.0f, z) * radius; 411 normals[vi] = Vector3f(0.0f, 1.0f, 0.0f); 412 texcoords[vi] = Vector2f(x * 0.5f + 0.5f, z * 0.5f + 0.5f); 413 414 indices[i][0] = 0; 415 if (s < slices - 1) 416 indices[i][1] = vi + 1; 417 else 418 indices[i][1] = indices[0][2]; 419 indices[i][2] = vi; 420 421 vi++; 422 i++; 423 424 angle += angleStep; 425 } 426 427 dataReady = true; 428 prepareVAO(); 429 } 430 } 431 432 // TODO: ShapeCylinder, ShapeCone, ShapeCapsule