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