| 1 | /** |
| 2 | * Copyright (c) 2006-2023 LOVE Development Team |
| 3 | * |
| 4 | * This software is provided 'as-is', without any express or implied |
| 5 | * warranty. In no event will the authors be held liable for any damages |
| 6 | * arising from the use of this software. |
| 7 | * |
| 8 | * Permission is granted to anyone to use this software for any purpose, |
| 9 | * including commercial applications, and to alter it and redistribute it |
| 10 | * freely, subject to the following restrictions: |
| 11 | * |
| 12 | * 1. The origin of this software must not be misrepresented; you must not |
| 13 | * claim that you wrote the original software. If you use this software |
| 14 | * in a product, an acknowledgment in the product documentation would be |
| 15 | * appreciated but is not required. |
| 16 | * 2. Altered source versions must be plainly marked as such, and must not be |
| 17 | * misrepresented as being the original software. |
| 18 | * 3. This notice may not be removed or altered from any source distribution. |
| 19 | **/ |
| 20 | |
| 21 | // LOVE |
| 22 | #include "Mesh.h" |
| 23 | #include "common/Matrix.h" |
| 24 | #include "common/Exception.h" |
| 25 | #include "Shader.h" |
| 26 | #include "Graphics.h" |
| 27 | |
| 28 | // C++ |
| 29 | #include <algorithm> |
| 30 | #include <limits> |
| 31 | |
| 32 | namespace love |
| 33 | { |
| 34 | namespace graphics |
| 35 | { |
| 36 | |
| 37 | static const char *getBuiltinAttribName(BuiltinVertexAttribute attribid) |
| 38 | { |
| 39 | const char *name = "" ; |
| 40 | vertex::getConstant(attribid, name); |
| 41 | return name; |
| 42 | } |
| 43 | |
| 44 | static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct" ); |
| 45 | static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct" ); |
| 46 | static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct" ); |
| 47 | |
| 48 | std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat() |
| 49 | { |
| 50 | // Corresponds to the love::Vertex struct. |
| 51 | std::vector<Mesh::AttribFormat> vertexformat = { |
| 52 | { getBuiltinAttribName(ATTRIB_POS), vertex::DATA_FLOAT, 2 }, |
| 53 | { getBuiltinAttribName(ATTRIB_TEXCOORD), vertex::DATA_FLOAT, 2 }, |
| 54 | { getBuiltinAttribName(ATTRIB_COLOR), vertex::DATA_UNORM8, 4 }, |
| 55 | }; |
| 56 | |
| 57 | return vertexformat; |
| 58 | } |
| 59 | |
| 60 | love::Type Mesh::type("Mesh" , &Drawable::type); |
| 61 | |
| 62 | Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage) |
| 63 | : vertexFormat(vertexformat) |
| 64 | , vertexBuffer(nullptr) |
| 65 | , vertexCount(0) |
| 66 | , vertexStride(0) |
| 67 | , indexBuffer(nullptr) |
| 68 | , useIndexBuffer(false) |
| 69 | , indexCount(0) |
| 70 | , indexDataType(INDEX_UINT16) |
| 71 | , primitiveType(drawmode) |
| 72 | , rangeStart(-1) |
| 73 | , rangeCount(-1) |
| 74 | { |
| 75 | setupAttachedAttributes(); |
| 76 | calculateAttributeSizes(); |
| 77 | |
| 78 | vertexCount = datasize / vertexStride; |
| 79 | indexDataType = vertex::getIndexDataTypeFromMax(vertexCount); |
| 80 | |
| 81 | if (vertexCount == 0) |
| 82 | throw love::Exception("Data size is too small for specified vertex attribute formats." ); |
| 83 | |
| 84 | vertexBuffer = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ); |
| 85 | |
| 86 | vertexScratchBuffer = new char[vertexStride]; |
| 87 | } |
| 88 | |
| 89 | Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage) |
| 90 | : vertexFormat(vertexformat) |
| 91 | , vertexBuffer(nullptr) |
| 92 | , vertexCount((size_t) vertexcount) |
| 93 | , vertexStride(0) |
| 94 | , indexBuffer(nullptr) |
| 95 | , useIndexBuffer(false) |
| 96 | , indexCount(0) |
| 97 | , indexDataType(vertex::getIndexDataTypeFromMax(vertexcount)) |
| 98 | , primitiveType(drawmode) |
| 99 | , rangeStart(-1) |
| 100 | , rangeCount(-1) |
| 101 | { |
| 102 | if (vertexcount <= 0) |
| 103 | throw love::Exception("Invalid number of vertices (%d)." , vertexcount); |
| 104 | |
| 105 | setupAttachedAttributes(); |
| 106 | calculateAttributeSizes(); |
| 107 | |
| 108 | size_t buffersize = vertexCount * vertexStride; |
| 109 | |
| 110 | vertexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ); |
| 111 | |
| 112 | // Initialize the buffer's contents to 0. |
| 113 | memset(vertexBuffer->map(), 0, buffersize); |
| 114 | vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize()); |
| 115 | vertexBuffer->unmap(); |
| 116 | |
| 117 | vertexScratchBuffer = new char[vertexStride]; |
| 118 | } |
| 119 | |
| 120 | Mesh::~Mesh() |
| 121 | { |
| 122 | delete vertexBuffer; |
| 123 | delete indexBuffer; |
| 124 | delete[] vertexScratchBuffer; |
| 125 | |
| 126 | for (const auto &attrib : attachedAttributes) |
| 127 | { |
| 128 | if (attrib.second.mesh != this) |
| 129 | attrib.second.mesh->release(); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | void Mesh::setupAttachedAttributes() |
| 134 | { |
| 135 | for (size_t i = 0; i < vertexFormat.size(); i++) |
| 136 | { |
| 137 | const std::string &name = vertexFormat[i].name; |
| 138 | |
| 139 | if (attachedAttributes.find(name) != attachedAttributes.end()) |
| 140 | throw love::Exception("Duplicate vertex attribute name: %s" , name.c_str()); |
| 141 | |
| 142 | attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true}; |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | void Mesh::calculateAttributeSizes() |
| 147 | { |
| 148 | size_t stride = 0; |
| 149 | |
| 150 | for (const AttribFormat &format : vertexFormat) |
| 151 | { |
| 152 | size_t size = vertex::getDataTypeSize(format.type) * format.components; |
| 153 | |
| 154 | if (format.components <= 0 || format.components > 4) |
| 155 | throw love::Exception("Vertex attributes must have between 1 and 4 components." ); |
| 156 | |
| 157 | // Hardware really doesn't like attributes that aren't 32 bit-aligned. |
| 158 | if (size % 4 != 0) |
| 159 | throw love::Exception("Vertex attributes must have enough components to be a multiple of 32 bits." ); |
| 160 | |
| 161 | // Total size in bytes of each attribute in a single vertex. |
| 162 | attributeSizes.push_back(size); |
| 163 | stride += size; |
| 164 | } |
| 165 | |
| 166 | vertexStride = stride; |
| 167 | } |
| 168 | |
| 169 | size_t Mesh::getAttributeOffset(size_t attribindex) const |
| 170 | { |
| 171 | size_t offset = 0; |
| 172 | |
| 173 | for (size_t i = 0; i < attribindex; i++) |
| 174 | offset += attributeSizes[i]; |
| 175 | |
| 176 | return offset; |
| 177 | } |
| 178 | |
| 179 | void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize) |
| 180 | { |
| 181 | if (vertindex >= vertexCount) |
| 182 | throw love::Exception("Invalid vertex index: %ld" , vertindex + 1); |
| 183 | |
| 184 | size_t offset = vertindex * vertexStride; |
| 185 | size_t size = std::min(datasize, vertexStride); |
| 186 | |
| 187 | uint8 *bufferdata = (uint8 *) vertexBuffer->map(); |
| 188 | memcpy(bufferdata + offset, data, size); |
| 189 | |
| 190 | vertexBuffer->setMappedRangeModified(offset, size); |
| 191 | } |
| 192 | |
| 193 | size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize) |
| 194 | { |
| 195 | if (vertindex >= vertexCount) |
| 196 | throw love::Exception("Invalid vertex index: %ld" , vertindex + 1); |
| 197 | |
| 198 | size_t offset = vertindex * vertexStride; |
| 199 | size_t size = std::min(datasize, vertexStride); |
| 200 | |
| 201 | // We're relying on map() returning read/write data... ew. |
| 202 | const uint8 *bufferdata = (const uint8 *) vertexBuffer->map(); |
| 203 | memcpy(data, bufferdata + offset, size); |
| 204 | |
| 205 | return size; |
| 206 | } |
| 207 | |
| 208 | void *Mesh::getVertexScratchBuffer() |
| 209 | { |
| 210 | return vertexScratchBuffer; |
| 211 | } |
| 212 | |
| 213 | void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *data, size_t datasize) |
| 214 | { |
| 215 | if (vertindex >= vertexCount) |
| 216 | throw love::Exception("Invalid vertex index: %ld" , vertindex + 1); |
| 217 | |
| 218 | if (attribindex >= (int) vertexFormat.size()) |
| 219 | throw love::Exception("Invalid vertex attribute index: %d" , attribindex + 1); |
| 220 | |
| 221 | size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex); |
| 222 | size_t size = std::min(datasize, attributeSizes[attribindex]); |
| 223 | |
| 224 | uint8 *bufferdata = (uint8 *) vertexBuffer->map(); |
| 225 | memcpy(bufferdata + offset, data, size); |
| 226 | |
| 227 | vertexBuffer->setMappedRangeModified(offset, size); |
| 228 | } |
| 229 | |
| 230 | size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, size_t datasize) |
| 231 | { |
| 232 | if (vertindex >= vertexCount) |
| 233 | throw love::Exception("Invalid vertex index: %ld" , vertindex + 1); |
| 234 | |
| 235 | if (attribindex >= (int) vertexFormat.size()) |
| 236 | throw love::Exception("Invalid vertex attribute index: %d" , attribindex + 1); |
| 237 | |
| 238 | size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex); |
| 239 | size_t size = std::min(datasize, attributeSizes[attribindex]); |
| 240 | |
| 241 | // We're relying on map() returning read/write data... ew. |
| 242 | const uint8 *bufferdata = (const uint8 *) vertexBuffer->map(); |
| 243 | memcpy(data, bufferdata + offset, size); |
| 244 | |
| 245 | return size; |
| 246 | } |
| 247 | |
| 248 | size_t Mesh::getVertexCount() const |
| 249 | { |
| 250 | return vertexCount; |
| 251 | } |
| 252 | |
| 253 | size_t Mesh::getVertexStride() const |
| 254 | { |
| 255 | return vertexStride; |
| 256 | } |
| 257 | |
| 258 | const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const |
| 259 | { |
| 260 | return vertexFormat; |
| 261 | } |
| 262 | |
| 263 | vertex::DataType Mesh::getAttributeInfo(int attribindex, int &components) const |
| 264 | { |
| 265 | if (attribindex < 0 || attribindex >= (int) vertexFormat.size()) |
| 266 | throw love::Exception("Invalid vertex attribute index: %d" , attribindex + 1); |
| 267 | |
| 268 | components = vertexFormat[attribindex].components; |
| 269 | return vertexFormat[attribindex].type; |
| 270 | } |
| 271 | |
| 272 | int Mesh::getAttributeIndex(const std::string &name) const |
| 273 | { |
| 274 | for (int i = 0; i < (int) vertexFormat.size(); i++) |
| 275 | { |
| 276 | if (vertexFormat[i].name == name) |
| 277 | return i; |
| 278 | } |
| 279 | |
| 280 | return -1; |
| 281 | } |
| 282 | |
| 283 | void Mesh::setAttributeEnabled(const std::string &name, bool enable) |
| 284 | { |
| 285 | auto it = attachedAttributes.find(name); |
| 286 | |
| 287 | if (it == attachedAttributes.end()) |
| 288 | throw love::Exception("Mesh does not have an attached vertex attribute named '%s'" , name.c_str()); |
| 289 | |
| 290 | it->second.enabled = enable; |
| 291 | } |
| 292 | |
| 293 | bool Mesh::isAttributeEnabled(const std::string &name) const |
| 294 | { |
| 295 | const auto it = attachedAttributes.find(name); |
| 296 | |
| 297 | if (it == attachedAttributes.end()) |
| 298 | throw love::Exception("Mesh does not have an attached vertex attribute named '%s'" , name.c_str()); |
| 299 | |
| 300 | return it->second.enabled; |
| 301 | } |
| 302 | |
| 303 | void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step) |
| 304 | { |
| 305 | auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS); |
| 306 | if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING]) |
| 307 | throw love::Exception("Vertex attribute instancing is not supported on this system." ); |
| 308 | |
| 309 | if (mesh != this) |
| 310 | { |
| 311 | for (const auto &it : mesh->attachedAttributes) |
| 312 | { |
| 313 | // If the supplied Mesh has attached attributes of its own, then we |
| 314 | // prevent it from being attached to avoid reference cycles. |
| 315 | if (it.second.mesh != mesh) |
| 316 | throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own." ); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | AttachedAttribute oldattrib = {}; |
| 321 | AttachedAttribute newattrib = {}; |
| 322 | |
| 323 | auto it = attachedAttributes.find(name); |
| 324 | if (it != attachedAttributes.end()) |
| 325 | oldattrib = it->second; |
| 326 | else if (attachedAttributes.size() + 1 > vertex::Attributes::MAX) |
| 327 | throw love::Exception("A maximum of %d attributes can be attached at once." , vertex::Attributes::MAX); |
| 328 | |
| 329 | newattrib.mesh = mesh; |
| 330 | newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true; |
| 331 | newattrib.index = mesh->getAttributeIndex(attachname); |
| 332 | newattrib.step = step; |
| 333 | |
| 334 | if (newattrib.index < 0) |
| 335 | throw love::Exception("The specified mesh does not have a vertex attribute named '%s'" , attachname.c_str()); |
| 336 | |
| 337 | if (newattrib.mesh != this) |
| 338 | newattrib.mesh->retain(); |
| 339 | |
| 340 | attachedAttributes[name] = newattrib; |
| 341 | |
| 342 | if (oldattrib.mesh && oldattrib.mesh != this) |
| 343 | oldattrib.mesh->release(); |
| 344 | } |
| 345 | |
| 346 | bool Mesh::detachAttribute(const std::string &name) |
| 347 | { |
| 348 | auto it = attachedAttributes.find(name); |
| 349 | |
| 350 | if (it != attachedAttributes.end() && it->second.mesh != this) |
| 351 | { |
| 352 | it->second.mesh->release(); |
| 353 | attachedAttributes.erase(it); |
| 354 | |
| 355 | if (getAttributeIndex(name) != -1) |
| 356 | attachAttribute(name, this, name); |
| 357 | |
| 358 | return true; |
| 359 | } |
| 360 | |
| 361 | return false; |
| 362 | } |
| 363 | |
| 364 | void *Mesh::mapVertexData() |
| 365 | { |
| 366 | return vertexBuffer->map(); |
| 367 | } |
| 368 | |
| 369 | void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize) |
| 370 | { |
| 371 | vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize); |
| 372 | vertexBuffer->unmap(); |
| 373 | } |
| 374 | |
| 375 | void Mesh::flush() |
| 376 | { |
| 377 | vertexBuffer->unmap(); |
| 378 | |
| 379 | if (indexBuffer != nullptr) |
| 380 | indexBuffer->unmap(); |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Copies index data from a vector to a mapped index buffer. |
| 385 | **/ |
| 386 | template <typename T> |
| 387 | static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval) |
| 388 | { |
| 389 | T *elems = (T *) buffermap.get(); |
| 390 | |
| 391 | for (size_t i = 0; i < indices.size(); i++) |
| 392 | { |
| 393 | if (indices[i] >= maxval) |
| 394 | throw love::Exception("Invalid vertex map value: %d" , indices[i] + 1); |
| 395 | |
| 396 | elems[i] = (T) indices[i]; |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | void Mesh::setVertexMap(const std::vector<uint32> &map) |
| 401 | { |
| 402 | size_t maxval = getVertexCount(); |
| 403 | |
| 404 | IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval); |
| 405 | |
| 406 | // Calculate the size in bytes of the index buffer data. |
| 407 | size_t size = map.size() * vertex::getIndexDataSize(datatype); |
| 408 | |
| 409 | if (indexBuffer && size > indexBuffer->getSize()) |
| 410 | { |
| 411 | delete indexBuffer; |
| 412 | indexBuffer = nullptr; |
| 413 | } |
| 414 | |
| 415 | if (!indexBuffer && size > 0) |
| 416 | { |
| 417 | auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS); |
| 418 | indexBuffer = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ); |
| 419 | } |
| 420 | |
| 421 | useIndexBuffer = true; |
| 422 | indexCount = map.size(); |
| 423 | |
| 424 | if (!indexBuffer || indexCount == 0) |
| 425 | return; |
| 426 | |
| 427 | Buffer::Mapper ibomap(*indexBuffer); |
| 428 | |
| 429 | // Fill the buffer with the index values from the vector. |
| 430 | switch (datatype) |
| 431 | { |
| 432 | case INDEX_UINT16: |
| 433 | copyToIndexBuffer<uint16>(map, ibomap, maxval); |
| 434 | break; |
| 435 | case INDEX_UINT32: |
| 436 | default: |
| 437 | copyToIndexBuffer<uint32>(map, ibomap, maxval); |
| 438 | break; |
| 439 | } |
| 440 | |
| 441 | indexDataType = datatype; |
| 442 | } |
| 443 | |
| 444 | void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize) |
| 445 | { |
| 446 | if (indexBuffer && datasize > indexBuffer->getSize()) |
| 447 | { |
| 448 | delete indexBuffer; |
| 449 | indexBuffer = nullptr; |
| 450 | } |
| 451 | |
| 452 | if (!indexBuffer && datasize > 0) |
| 453 | { |
| 454 | auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS); |
| 455 | indexBuffer = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ); |
| 456 | } |
| 457 | |
| 458 | indexCount = datasize / vertex::getIndexDataSize(datatype); |
| 459 | |
| 460 | if (!indexBuffer || indexCount == 0) |
| 461 | return; |
| 462 | |
| 463 | Buffer::Mapper ibomap(*indexBuffer); |
| 464 | memcpy(ibomap.get(), data, datasize); |
| 465 | |
| 466 | useIndexBuffer = true; |
| 467 | indexDataType = datatype; |
| 468 | } |
| 469 | |
| 470 | void Mesh::setVertexMap() |
| 471 | { |
| 472 | useIndexBuffer = false; |
| 473 | } |
| 474 | |
| 475 | /** |
| 476 | * Copies index data from a mapped buffer to a vector. |
| 477 | **/ |
| 478 | template <typename T> |
| 479 | static void copyFromIndexBuffer(void *buffer, size_t count, std::vector<uint32> &indices) |
| 480 | { |
| 481 | T *elems = (T *) buffer; |
| 482 | for (size_t i = 0; i < count; i++) |
| 483 | indices.push_back((uint32) elems[i]); |
| 484 | } |
| 485 | |
| 486 | bool Mesh::getVertexMap(std::vector<uint32> &map) const |
| 487 | { |
| 488 | if (!useIndexBuffer) |
| 489 | return false; |
| 490 | |
| 491 | map.clear(); |
| 492 | map.reserve(indexCount); |
| 493 | |
| 494 | if (!indexBuffer || indexCount == 0) |
| 495 | return true; |
| 496 | |
| 497 | // We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush. |
| 498 | void *buffer = indexBuffer->map(); |
| 499 | |
| 500 | // Fill the vector from the buffer. |
| 501 | switch (indexDataType) |
| 502 | { |
| 503 | case INDEX_UINT16: |
| 504 | copyFromIndexBuffer<uint16>(buffer, indexCount, map); |
| 505 | break; |
| 506 | case INDEX_UINT32: |
| 507 | default: |
| 508 | copyFromIndexBuffer<uint32>(buffer, indexCount, map); |
| 509 | break; |
| 510 | } |
| 511 | |
| 512 | return true; |
| 513 | } |
| 514 | |
| 515 | size_t Mesh::getVertexMapCount() const |
| 516 | { |
| 517 | return indexCount; |
| 518 | } |
| 519 | |
| 520 | void Mesh::setTexture(Texture *tex) |
| 521 | { |
| 522 | texture.set(tex); |
| 523 | } |
| 524 | |
| 525 | void Mesh::setTexture() |
| 526 | { |
| 527 | texture.set(nullptr); |
| 528 | } |
| 529 | |
| 530 | Texture *Mesh::getTexture() const |
| 531 | { |
| 532 | return texture.get(); |
| 533 | } |
| 534 | |
| 535 | void Mesh::setDrawMode(PrimitiveType mode) |
| 536 | { |
| 537 | primitiveType = mode; |
| 538 | } |
| 539 | |
| 540 | PrimitiveType Mesh::getDrawMode() const |
| 541 | { |
| 542 | return primitiveType; |
| 543 | } |
| 544 | |
| 545 | void Mesh::setDrawRange(int start, int count) |
| 546 | { |
| 547 | if (start < 0 || count <= 0) |
| 548 | throw love::Exception("Invalid draw range." ); |
| 549 | |
| 550 | rangeStart = start; |
| 551 | rangeCount = count; |
| 552 | } |
| 553 | |
| 554 | void Mesh::setDrawRange() |
| 555 | { |
| 556 | rangeStart = rangeCount = -1; |
| 557 | } |
| 558 | |
| 559 | bool Mesh::getDrawRange(int &start, int &count) const |
| 560 | { |
| 561 | if (rangeStart < 0 || rangeCount <= 0) |
| 562 | return false; |
| 563 | |
| 564 | start = rangeStart; |
| 565 | count = rangeCount; |
| 566 | return true; |
| 567 | } |
| 568 | |
| 569 | void Mesh::draw(Graphics *gfx, const love::Matrix4 &m) |
| 570 | { |
| 571 | drawInstanced(gfx, m, 1); |
| 572 | } |
| 573 | |
| 574 | void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) |
| 575 | { |
| 576 | if (vertexCount <= 0 || instancecount <= 0) |
| 577 | return; |
| 578 | |
| 579 | if (instancecount > 1 && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING]) |
| 580 | throw love::Exception("Instancing is not supported on this system." ); |
| 581 | |
| 582 | gfx->flushStreamDraws(); |
| 583 | |
| 584 | if (Shader::isDefaultActive()) |
| 585 | Shader::attachDefault(Shader::STANDARD_DEFAULT); |
| 586 | |
| 587 | if (Shader::current && texture.get()) |
| 588 | Shader::current->checkMainTexture(texture); |
| 589 | |
| 590 | vertex::Attributes attributes; |
| 591 | vertex::BufferBindings buffers; |
| 592 | |
| 593 | int activebuffers = 0; |
| 594 | |
| 595 | for (const auto &attrib : attachedAttributes) |
| 596 | { |
| 597 | if (!attrib.second.enabled) |
| 598 | continue; |
| 599 | |
| 600 | Mesh *mesh = attrib.second.mesh; |
| 601 | int attributeindex = -1; |
| 602 | |
| 603 | // If the attribute is one of the LOVE-defined ones, use the constant |
| 604 | // attribute index for it, otherwise query the index from the shader. |
| 605 | BuiltinVertexAttribute builtinattrib; |
| 606 | if (vertex::getConstant(attrib.first.c_str(), builtinattrib)) |
| 607 | attributeindex = (int) builtinattrib; |
| 608 | else if (Shader::current) |
| 609 | attributeindex = Shader::current->getVertexAttributeIndex(attrib.first); |
| 610 | |
| 611 | if (attributeindex >= 0) |
| 612 | { |
| 613 | // Make sure the buffer isn't mapped (sends data to GPU if needed.) |
| 614 | mesh->vertexBuffer->unmap(); |
| 615 | |
| 616 | const auto &formats = mesh->getVertexFormat(); |
| 617 | const auto &format = formats[attrib.second.index]; |
| 618 | |
| 619 | uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index); |
| 620 | uint16 stride = (uint16) mesh->getVertexStride(); |
| 621 | |
| 622 | attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers); |
| 623 | attributes.setBufferLayout(activebuffers, stride, attrib.second.step); |
| 624 | |
| 625 | // TODO: Ideally we want to reuse buffers with the same stride+step. |
| 626 | buffers.set(activebuffers, mesh->vertexBuffer, 0); |
| 627 | activebuffers++; |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | // Not supported on all platforms or GL versions, I believe. |
| 632 | if (!attributes.isEnabled(ATTRIB_POS)) |
| 633 | throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn." ); |
| 634 | |
| 635 | Graphics::TempTransform transform(gfx, m); |
| 636 | |
| 637 | if (useIndexBuffer && indexBuffer != nullptr && indexCount > 0) |
| 638 | { |
| 639 | // Make sure the index buffer isn't mapped (sends data to GPU if needed.) |
| 640 | indexBuffer->unmap(); |
| 641 | |
| 642 | Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer); |
| 643 | |
| 644 | cmd.primitiveType = primitiveType; |
| 645 | cmd.indexType = indexDataType; |
| 646 | cmd.instanceCount = instancecount; |
| 647 | cmd.texture = texture; |
| 648 | cmd.cullMode = gfx->getMeshCullMode(); |
| 649 | |
| 650 | int start = std::min(std::max(0, rangeStart), (int) indexCount - 1); |
| 651 | cmd.indexBufferOffset = start * vertex::getIndexDataSize(indexDataType); |
| 652 | |
| 653 | cmd.indexCount = (int) indexCount; |
| 654 | if (rangeCount > 0) |
| 655 | cmd.indexCount = std::min(cmd.indexCount, rangeCount); |
| 656 | |
| 657 | cmd.indexCount = std::min(cmd.indexCount, (int) indexCount - start); |
| 658 | |
| 659 | if (cmd.indexCount > 0) |
| 660 | gfx->draw(cmd); |
| 661 | } |
| 662 | else if (vertexCount > 0) |
| 663 | { |
| 664 | Graphics::DrawCommand cmd(&attributes, &buffers); |
| 665 | |
| 666 | cmd.primitiveType = primitiveType; |
| 667 | cmd.vertexStart = std::min(std::max(0, rangeStart), (int) vertexCount - 1); |
| 668 | |
| 669 | cmd.vertexCount = (int) vertexCount; |
| 670 | if (rangeCount > 0) |
| 671 | cmd.vertexCount = std::min(cmd.vertexCount, rangeCount); |
| 672 | |
| 673 | cmd.vertexCount = std::min(cmd.vertexCount, (int) vertexCount - cmd.vertexStart); |
| 674 | cmd.instanceCount = instancecount; |
| 675 | cmd.texture = texture; |
| 676 | cmd.cullMode = gfx->getMeshCullMode(); |
| 677 | |
| 678 | if (cmd.vertexCount > 0) |
| 679 | gfx->draw(cmd); |
| 680 | } |
| 681 | } |
| 682 | |
| 683 | } // graphics |
| 684 | } // love |
| 685 | |