| 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 |  | 
|---|