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 | |
30 | namespace love |
31 | { |
32 | namespace graphics |
33 | { |
34 | |
35 | Mesh *luax_checkmesh(lua_State *L, int idx) |
36 | { |
37 | return luax_checktype<Mesh>(L, idx); |
38 | } |
39 | |
40 | static 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 | |
50 | static 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 | |
60 | static 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 | |
70 | char *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 | |
85 | static 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 | |
95 | static 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 | |
105 | static 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 | |
115 | const 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 | |
130 | int 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 | |
209 | int 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 | |
251 | int 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 | |
274 | int 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 | |
294 | int 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 | |
313 | int 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 | |
320 | int 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 | |
352 | int 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 | |
361 | int 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 | |
371 | int 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 | |
388 | int 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 | |
398 | int w_Mesh_flush(lua_State *L) |
399 | { |
400 | Mesh *t = luax_checkmesh(L, 1); |
401 | t->flush(); |
402 | return 0; |
403 | } |
404 | |
405 | int 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 | |
461 | int 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 | |
488 | int 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 | |
503 | int 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 | |
522 | int 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 | |
535 | int 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 | |
548 | int 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 | |
564 | int 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 | |
578 | static 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 | |
603 | extern "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 | |