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 "wrap_Mesh.h"
23#include "Image.h"
24#include "Canvas.h"
25#include "wrap_Texture.h"
26
27// C++
28#include <algorithm>
29
30namespace love
31{
32namespace graphics
33{
34
35Mesh *luax_checkmesh(lua_State *L, int idx)
36{
37 return luax_checktype<Mesh>(L, idx);
38}
39
40static inline size_t writeUnorm8Data(lua_State *L, int startidx, int components, char *data)
41{
42 uint8 *componentdata = (uint8 *) data;
43
44 for (int i = 0; i < components; i++)
45 componentdata[i] = (uint8) (luax_optnumberclamped01(L, startidx + i, 1.0) * 255.0);
46
47 return sizeof(uint8) * components;
48}
49
50static inline size_t writeUnorm16Data(lua_State *L, int startidx, int components, char *data)
51{
52 uint16 *componentdata = (uint16 *) data;
53
54 for (int i = 0; i < components; i++)
55 componentdata[i] = (uint16) (luax_optnumberclamped01(L, startidx + i, 1.0) * 65535.0);
56
57 return sizeof(uint16) * components;
58}
59
60static inline size_t writeFloatData(lua_State *L, int startidx, int components, char *data)
61{
62 float *componentdata = (float *) data;
63
64 for (int i = 0; i < components; i++)
65 componentdata[i] = (float) luaL_optnumber(L, startidx + i, 0);
66
67 return sizeof(float) * components;
68}
69
70char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data)
71{
72 switch (type)
73 {
74 case vertex::DATA_UNORM8:
75 return data + writeUnorm8Data(L, startidx, components, data);
76 case vertex::DATA_UNORM16:
77 return data + writeUnorm16Data(L, startidx, components, data);
78 case vertex::DATA_FLOAT:
79 return data + writeFloatData(L, startidx, components, data);
80 default:
81 return data;
82 }
83}
84
85static inline size_t readUnorm8Data(lua_State *L, int components, const char *data)
86{
87 const uint8 *componentdata = (const uint8 *) data;
88
89 for (int i = 0; i < components; i++)
90 lua_pushnumber(L, (lua_Number) componentdata[i] / 255.0);
91
92 return sizeof(uint8) * components;
93}
94
95static inline size_t readUnorm16Data(lua_State *L, int components, const char *data)
96{
97 const uint16 *componentdata = (const uint16 *) data;
98
99 for (int i = 0; i < components; i++)
100 lua_pushnumber(L, (lua_Number) componentdata[i] / 65535.0);
101
102 return sizeof(uint16) * components;
103}
104
105static inline size_t readFloatData(lua_State *L, int components, const char *data)
106{
107 const float *componentdata = (const float *) data;
108
109 for (int i = 0; i < components; i++)
110 lua_pushnumber(L, componentdata[i]);
111
112 return sizeof(float) * components;
113}
114
115const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data)
116{
117 switch (type)
118 {
119 case vertex::DATA_UNORM8:
120 return data + readUnorm8Data(L, components, data);
121 case vertex::DATA_UNORM16:
122 return data + readUnorm16Data(L, components, data);
123 case vertex::DATA_FLOAT:
124 return data + readFloatData(L, components, data);
125 default:
126 return data;
127 }
128}
129
130int w_Mesh_setVertices(lua_State *L)
131{
132 Mesh *t = luax_checkmesh(L, 1);
133
134 int vertstart = (int) luaL_optnumber(L, 3, 1) - 1;
135
136 int vertcount = -1;
137 if (!lua_isnoneornil(L, 4))
138 {
139 vertcount = (int) luaL_checknumber(L, 4);
140 if (vertcount <= 0)
141 return luaL_error(L, "Vertex count must be greater than 0.");
142 }
143
144 size_t stride = t->getVertexStride();
145 size_t byteoffset = vertstart * stride;
146 int totalverts = (int) t->getVertexCount();
147
148 if (vertstart >= totalverts)
149 return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", totalverts);
150
151 if (luax_istype(L, 2, Data::type))
152 {
153 Data *d = luax_checktype<Data>(L, 2);
154
155 vertcount = vertcount >= 0 ? vertcount : (totalverts - vertstart);
156 if (vertstart + vertcount > totalverts)
157 return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
158
159 size_t datasize = std::min(d->getSize(), vertcount * stride);
160 char *bytedata = (char *) t->mapVertexData() + byteoffset;
161
162 memcpy(bytedata, d->getData(), datasize);
163
164 t->unmapVertexData(byteoffset, datasize);
165 return 0;
166 }
167
168 luaL_checktype(L, 2, LUA_TTABLE);
169 int tablelen = (int) luax_objlen(L, 2);
170
171 vertcount = vertcount >= 0 ? std::min(vertcount, tablelen) : tablelen;
172 if (vertstart + vertcount > totalverts)
173 return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
174
175 const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
176
177 int ncomponents = 0;
178 for (const Mesh::AttribFormat &format : vertexformat)
179 ncomponents += format.components;
180
181 char *data = (char *) t->mapVertexData() + byteoffset;
182
183 for (int i = 0; i < vertcount; i++)
184 {
185 // get vertices[vertindex]
186 lua_rawgeti(L, 2, i + 1);
187 luaL_checktype(L, -1, LUA_TTABLE);
188
189 // get vertices[vertindex][j]
190 for (int j = 1; j <= ncomponents; j++)
191 lua_rawgeti(L, -j, j);
192
193 int idx = -ncomponents;
194
195 for (const Mesh::AttribFormat &format : vertexformat)
196 {
197 // Fetch the values from Lua and store them in data buffer.
198 data = luax_writeAttributeData(L, idx, format.type, format.components, data);
199 idx += format.components;
200 }
201
202 lua_pop(L, ncomponents + 1);
203 }
204
205 t->unmapVertexData(byteoffset, vertcount * stride);
206 return 0;
207}
208
209int w_Mesh_setVertex(lua_State *L)
210{
211 Mesh *t = luax_checkmesh(L, 1);
212 size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
213
214 bool istable = lua_istable(L, 3);
215
216 const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
217
218 char *data = (char *) t->getVertexScratchBuffer();
219 char *writtendata = data;
220
221 int idx = istable ? 1 : 3;
222
223 if (istable)
224 {
225 for (const Mesh::AttribFormat &format : vertexformat)
226 {
227 for (int i = idx; i < idx + format.components; i++)
228 lua_rawgeti(L, 3, i);
229
230 // Fetch the values from Lua and store them in data buffer.
231 writtendata = luax_writeAttributeData(L, -format.components, format.type, format.components, writtendata);
232
233 idx += format.components;
234 lua_pop(L, format.components);
235 }
236 }
237 else
238 {
239 for (const Mesh::AttribFormat &format : vertexformat)
240 {
241 // Fetch the values from Lua and store them in data buffer.
242 writtendata = luax_writeAttributeData(L, idx, format.type, format.components, writtendata);
243 idx += format.components;
244 }
245 }
246
247 luax_catchexcept(L, [&](){ t->setVertex(index, data, t->getVertexStride()); });
248 return 0;
249}
250
251int w_Mesh_getVertex(lua_State *L)
252{
253 Mesh *t = luax_checkmesh(L, 1);
254 size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
255
256 const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
257
258 char *data = (char *) t->getVertexScratchBuffer();
259 const char *readdata = data;
260
261 luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); });
262
263 int n = 0;
264
265 for (const Mesh::AttribFormat &format : vertexformat)
266 {
267 readdata = luax_readAttributeData(L, format.type, format.components, readdata);
268 n += format.components;
269 }
270
271 return n;
272}
273
274int w_Mesh_setVertexAttribute(lua_State *L)
275{
276 Mesh *t = luax_checkmesh(L, 1);
277 size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
278 int attribindex = (int) luaL_checkinteger(L, 3) - 1;
279
280 vertex::DataType type;
281 int components;
282 luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
283
284 // Maximum possible size for a single vertex attribute.
285 char data[sizeof(float) * 4];
286
287 // Fetch the values from Lua and store them in the data buffer.
288 luax_writeAttributeData(L, 4, type, components, data);
289
290 luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
291 return 0;
292}
293
294int w_Mesh_getVertexAttribute(lua_State *L)
295{
296 Mesh *t = luax_checkmesh(L, 1);
297 size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
298 int attribindex = (int) luaL_checkinteger(L, 3) - 1;
299
300 vertex::DataType type;
301 int components;
302 luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
303
304 // Maximum possible size for a single vertex attribute.
305 char data[sizeof(float) * 4];
306
307 luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
308
309 luax_readAttributeData(L, type, components, data);
310 return components;
311}
312
313int w_Mesh_getVertexCount(lua_State *L)
314{
315 Mesh *t = luax_checkmesh(L, 1);
316 lua_pushinteger(L, t->getVertexCount());
317 return 1;
318}
319
320int w_Mesh_getVertexFormat(lua_State *L)
321{
322 Mesh *t = luax_checkmesh(L, 1);
323
324 const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
325 lua_createtable(L, (int) vertexformat.size(), 0);
326
327 const char *tname = nullptr;
328
329 for (size_t i = 0; i < vertexformat.size(); i++)
330 {
331 if (!vertex::getConstant(vertexformat[i].type, tname))
332 return luax_enumerror(L, "vertex attribute data type", vertex::getConstants(vertexformat[i].type), tname);
333
334 lua_createtable(L, 3, 0);
335
336 lua_pushstring(L, vertexformat[i].name.c_str());
337 lua_rawseti(L, -2, 1);
338
339 lua_pushstring(L, tname);
340 lua_rawseti(L, -2, 2);
341
342 lua_pushinteger(L, vertexformat[i].components);
343 lua_rawseti(L, -2, 3);
344
345 // format[i] = {name, type, components}
346 lua_rawseti(L, -2, (int) i + 1);
347 }
348
349 return 1;
350}
351
352int w_Mesh_setAttributeEnabled(lua_State *L)
353{
354 Mesh *t = luax_checkmesh(L, 1);
355 const char *name = luaL_checkstring(L, 2);
356 bool enable = luax_checkboolean(L, 3);
357 luax_catchexcept(L, [&](){ t->setAttributeEnabled(name, enable); });
358 return 0;
359}
360
361int w_Mesh_isAttributeEnabled(lua_State *L)
362{
363 Mesh *t = luax_checkmesh(L, 1);
364 const char *name = luaL_checkstring(L, 2);
365 bool enabled = false;
366 luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(name); });
367 lua_pushboolean(L, enabled);
368 return 1;
369}
370
371int w_Mesh_attachAttribute(lua_State *L)
372{
373 Mesh *t = luax_checkmesh(L, 1);
374 const char *name = luaL_checkstring(L, 2);
375 Mesh *mesh = luax_checkmesh(L, 3);
376
377 AttributeStep step = STEP_PER_VERTEX;
378 const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
379 if (stepstr != nullptr && !vertex::getConstant(stepstr, step))
380 return luax_enumerror(L, "vertex attribute step", vertex::getConstants(step), stepstr);
381
382 const char *attachname = luaL_optstring(L, 5, name);
383
384 luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname, step); });
385 return 0;
386}
387
388int w_Mesh_detachAttribute(lua_State *L)
389{
390 Mesh *t = luax_checkmesh(L, 1);
391 const char *name = luaL_checkstring(L, 2);
392 bool success = false;
393 luax_catchexcept(L, [&](){ success = t->detachAttribute(name); });
394 luax_pushboolean(L, success);
395 return 1;
396}
397
398int w_Mesh_flush(lua_State *L)
399{
400 Mesh *t = luax_checkmesh(L, 1);
401 t->flush();
402 return 0;
403}
404
405int w_Mesh_setVertexMap(lua_State *L)
406{
407 Mesh *t = luax_checkmesh(L, 1);
408
409 if (lua_isnoneornil(L, 2))
410 {
411 // Disable the vertex map / index buffer.
412 luax_catchexcept(L, [&](){ t->setVertexMap(); });
413 return 0;
414 }
415
416 if (luax_istype(L, 2, Data::type))
417 {
418 Data *d = luax_totype<Data>(L, 2, Data::type);
419
420 const char *indextypestr = luaL_checkstring(L, 3);
421 IndexDataType indextype;
422 if (!vertex::getConstant(indextypestr, indextype))
423 return luax_enumerror(L, "index data type", vertex::getConstants(indextype), indextypestr);
424
425 size_t datatypesize = vertex::getIndexDataSize(indextype);
426
427 int indexcount = (int) luaL_optinteger(L, 4, d->getSize() / datatypesize);
428
429 if (indexcount < 1 || indexcount * datatypesize > d->getSize())
430 return luaL_error(L, "Invalid index count: %d", indexcount);
431
432 luax_catchexcept(L, [&]() { t->setVertexMap(indextype, d->getData(), indexcount * datatypesize); });
433 return 0;
434 }
435
436 bool is_table = lua_istable(L, 2);
437 int nargs = is_table ? (int) luax_objlen(L, 2) : lua_gettop(L) - 1;
438
439 std::vector<uint32> vertexmap;
440 vertexmap.reserve(nargs);
441
442 if (is_table)
443 {
444 for (int i = 0; i < nargs; i++)
445 {
446 lua_rawgeti(L, 2, i + 1);
447 vertexmap.push_back(uint32(luaL_checkinteger(L, -1) - 1));
448 lua_pop(L, 1);
449 }
450 }
451 else
452 {
453 for (int i = 0; i < nargs; i++)
454 vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1));
455 }
456
457 luax_catchexcept(L, [&](){ t->setVertexMap(vertexmap); });
458 return 0;
459}
460
461int w_Mesh_getVertexMap(lua_State *L)
462{
463 Mesh *t = luax_checkmesh(L, 1);
464
465 std::vector<uint32> vertex_map;
466 bool has_vertex_map = false;
467 luax_catchexcept(L, [&](){ has_vertex_map = t->getVertexMap(vertex_map); });
468
469 if (!has_vertex_map)
470 {
471 lua_pushnil(L);
472 return 1;
473 }
474
475 int element_count = (int) vertex_map.size();
476
477 lua_createtable(L, element_count, 0);
478
479 for (int i = 0; i < element_count; i++)
480 {
481 lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
482 lua_rawseti(L, -2, i + 1);
483 }
484
485 return 1;
486}
487
488int w_Mesh_setTexture(lua_State *L)
489{
490 Mesh *t = luax_checkmesh(L, 1);
491
492 if (lua_isnoneornil(L, 2))
493 t->setTexture();
494 else
495 {
496 Texture *tex = luax_checktexture(L, 2);
497 luax_catchexcept(L, [&](){ t->setTexture(tex); });
498 }
499
500 return 0;
501}
502
503int w_Mesh_getTexture(lua_State *L)
504{
505 Mesh *t = luax_checkmesh(L, 1);
506 Texture *tex = t->getTexture();
507
508 if (tex == nullptr)
509 return 0;
510
511 // FIXME: big hack right here.
512 if (dynamic_cast<Image *>(tex) != nullptr)
513 luax_pushtype(L, Image::type, tex);
514 else if (dynamic_cast<Canvas *>(tex) != nullptr)
515 luax_pushtype(L, Canvas::type, tex);
516 else
517 return luaL_error(L, "Unable to determine texture type.");
518
519 return 1;
520}
521
522int w_Mesh_setDrawMode(lua_State *L)
523{
524 Mesh *t = luax_checkmesh(L, 1);
525 const char *str = luaL_checkstring(L, 2);
526 PrimitiveType mode;
527
528 if (!vertex::getConstant(str, mode))
529 return luax_enumerror(L, "mesh draw mode", vertex::getConstants(mode), str);
530
531 t->setDrawMode(mode);
532 return 0;
533}
534
535int w_Mesh_getDrawMode(lua_State *L)
536{
537 Mesh *t = luax_checkmesh(L, 1);
538 PrimitiveType mode = t->getDrawMode();
539 const char *str;
540
541 if (!vertex::getConstant(mode, str))
542 return luaL_error(L, "Unknown mesh draw mode.");
543
544 lua_pushstring(L, str);
545 return 1;
546}
547
548int w_Mesh_setDrawRange(lua_State *L)
549{
550 Mesh *t = luax_checkmesh(L, 1);
551
552 if (lua_isnoneornil(L, 2))
553 t->setDrawRange();
554 else
555 {
556 int start = (int) luaL_checkinteger(L, 2) - 1;
557 int count = (int) luaL_checkinteger(L, 3);
558 luax_catchexcept(L, [&](){ t->setDrawRange(start, count); });
559 }
560
561 return 0;
562}
563
564int w_Mesh_getDrawRange(lua_State *L)
565{
566 Mesh *t = luax_checkmesh(L, 1);
567
568 int start = 0;
569 int count = 1;
570 if (!t->getDrawRange(start, count))
571 return 0;
572
573 lua_pushinteger(L, start + 1);
574 lua_pushinteger(L, count);
575 return 2;
576}
577
578static const luaL_Reg w_Mesh_functions[] =
579{
580 { "setVertices", w_Mesh_setVertices },
581 { "setVertex", w_Mesh_setVertex },
582 { "getVertex", w_Mesh_getVertex },
583 { "setVertexAttribute", w_Mesh_setVertexAttribute },
584 { "getVertexAttribute", w_Mesh_getVertexAttribute },
585 { "getVertexCount", w_Mesh_getVertexCount },
586 { "getVertexFormat", w_Mesh_getVertexFormat },
587 { "setAttributeEnabled", w_Mesh_setAttributeEnabled },
588 { "isAttributeEnabled", w_Mesh_isAttributeEnabled },
589 { "attachAttribute", w_Mesh_attachAttribute },
590 { "detachAttribute", w_Mesh_detachAttribute },
591 { "flush", w_Mesh_flush },
592 { "setVertexMap", w_Mesh_setVertexMap },
593 { "getVertexMap", w_Mesh_getVertexMap },
594 { "setTexture", w_Mesh_setTexture },
595 { "getTexture", w_Mesh_getTexture },
596 { "setDrawMode", w_Mesh_setDrawMode },
597 { "getDrawMode", w_Mesh_getDrawMode },
598 { "setDrawRange", w_Mesh_setDrawRange },
599 { "getDrawRange", w_Mesh_getDrawRange },
600 { 0, 0 }
601};
602
603extern "C" int luaopen_mesh(lua_State *L)
604{
605 return luax_register_type(L, &Mesh::type, w_Mesh_functions, nullptr);
606}
607
608} // graphics
609} // love
610