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
32namespace love
33{
34namespace graphics
35{
36
37static const char *getBuiltinAttribName(BuiltinVertexAttribute attribid)
38{
39 const char *name = "";
40 vertex::getConstant(attribid, name);
41 return name;
42}
43
44static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct");
45static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
46static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
47
48std::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
60love::Type Mesh::type("Mesh", &Drawable::type);
61
62Mesh::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
89Mesh::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
120Mesh::~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
133void 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
146void 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
169size_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
179void 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
193size_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
208void *Mesh::getVertexScratchBuffer()
209{
210 return vertexScratchBuffer;
211}
212
213void 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
230size_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
248size_t Mesh::getVertexCount() const
249{
250 return vertexCount;
251}
252
253size_t Mesh::getVertexStride() const
254{
255 return vertexStride;
256}
257
258const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
259{
260 return vertexFormat;
261}
262
263vertex::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
272int 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
283void 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
293bool 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
303void 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
346bool 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
364void *Mesh::mapVertexData()
365{
366 return vertexBuffer->map();
367}
368
369void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize)
370{
371 vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize);
372 vertexBuffer->unmap();
373}
374
375void 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 **/
386template <typename T>
387static 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
400void 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
444void 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
470void Mesh::setVertexMap()
471{
472 useIndexBuffer = false;
473}
474
475/**
476 * Copies index data from a mapped buffer to a vector.
477 **/
478template <typename T>
479static 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
486bool 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
515size_t Mesh::getVertexMapCount() const
516{
517 return indexCount;
518}
519
520void Mesh::setTexture(Texture *tex)
521{
522 texture.set(tex);
523}
524
525void Mesh::setTexture()
526{
527 texture.set(nullptr);
528}
529
530Texture *Mesh::getTexture() const
531{
532 return texture.get();
533}
534
535void Mesh::setDrawMode(PrimitiveType mode)
536{
537 primitiveType = mode;
538}
539
540PrimitiveType Mesh::getDrawMode() const
541{
542 return primitiveType;
543}
544
545void 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
554void Mesh::setDrawRange()
555{
556 rangeStart = rangeCount = -1;
557}
558
559bool 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
569void Mesh::draw(Graphics *gfx, const love::Matrix4 &m)
570{
571 drawInstanced(gfx, m, 1);
572}
573
574void 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