mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
604 lines
15 KiB
C++
604 lines
15 KiB
C++
/**
|
|
* Copyright (c) 2006-2023 LOVE Development Team
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
**/
|
|
|
|
// LOVE
|
|
#include "wrap_Mesh.h"
|
|
#include "wrap_Buffer.h"
|
|
#include "Texture.h"
|
|
#include "wrap_Texture.h"
|
|
|
|
// C++
|
|
#include <algorithm>
|
|
|
|
namespace love
|
|
{
|
|
namespace graphics
|
|
{
|
|
|
|
Mesh *luax_checkmesh(lua_State *L, int idx)
|
|
{
|
|
return luax_checktype<Mesh>(L, idx);
|
|
}
|
|
|
|
int w_Mesh_setVertices(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
int vertstart = (int) luaL_optnumber(L, 3, 1) - 1;
|
|
|
|
int vertcount = -1;
|
|
if (!lua_isnoneornil(L, 4))
|
|
{
|
|
vertcount = (int) luaL_checknumber(L, 4);
|
|
if (vertcount <= 0)
|
|
return luaL_error(L, "Vertex count must be greater than 0.");
|
|
}
|
|
|
|
size_t stride = t->getVertexStride();
|
|
size_t byteoffset = vertstart * stride;
|
|
int totalverts = (int) t->getVertexCount();
|
|
|
|
if (vertstart >= totalverts || vertstart < 0)
|
|
return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", totalverts);
|
|
|
|
if (luax_istype(L, 2, Data::type))
|
|
{
|
|
Data *d = luax_checktype<Data>(L, 2);
|
|
|
|
vertcount = vertcount >= 0 ? vertcount : (totalverts - vertstart);
|
|
if (vertstart + vertcount > totalverts)
|
|
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
|
|
|
|
size_t datasize = std::min(d->getSize(), vertcount * stride);
|
|
char *bytedata = (char *) t->getVertexData() + byteoffset;
|
|
|
|
memcpy(bytedata, d->getData(), datasize);
|
|
|
|
t->setVertexDataModified(byteoffset, datasize);
|
|
t->flush();
|
|
|
|
return 0;
|
|
}
|
|
|
|
luaL_checktype(L, 2, LUA_TTABLE);
|
|
int tablelen = (int) luax_objlen(L, 2);
|
|
|
|
vertcount = vertcount >= 0 ? std::min(vertcount, tablelen) : tablelen;
|
|
if (vertstart + vertcount > totalverts)
|
|
return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
|
|
|
|
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
|
|
|
int ncomponents = 0;
|
|
for (const Buffer::DataMember &member : vertexformat)
|
|
ncomponents += member.info.components;
|
|
|
|
char *data = (char *) t->getVertexData() + byteoffset;
|
|
|
|
for (int i = 0; i < vertcount; i++)
|
|
{
|
|
// get vertices[vertindex]
|
|
lua_rawgeti(L, 2, i + 1);
|
|
luaL_checktype(L, -1, LUA_TTABLE);
|
|
|
|
// get vertices[vertindex][j]
|
|
for (int j = 1; j <= ncomponents; j++)
|
|
lua_rawgeti(L, -j, j);
|
|
|
|
int idx = -ncomponents;
|
|
|
|
for (const Buffer::DataMember &member : vertexformat)
|
|
{
|
|
// Fetch the values from Lua and store them in data buffer.
|
|
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
|
idx += member.info.components;
|
|
}
|
|
|
|
lua_pop(L, ncomponents + 1);
|
|
|
|
data += stride;
|
|
}
|
|
|
|
t->setVertexDataModified(byteoffset, vertcount * stride);
|
|
t->flush();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_setVertex(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
|
|
|
|
bool istable = lua_istable(L, 3);
|
|
|
|
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
|
|
|
char *data = nullptr;
|
|
size_t offset = 0;
|
|
luax_catchexcept(L, [&](){ data = (char *) t->checkVertexDataOffset(index, &offset); });
|
|
|
|
int idx = istable ? 1 : 3;
|
|
|
|
if (istable)
|
|
{
|
|
for (const Buffer::DataMember &member : vertexformat)
|
|
{
|
|
int components = member.info.components;
|
|
|
|
for (int i = idx; i < idx + components; i++)
|
|
lua_rawgeti(L, 3, i);
|
|
|
|
// Fetch the values from Lua and store them in data buffer.
|
|
luax_writebufferdata(L, -components, member.decl.format, data + member.offset);
|
|
|
|
idx += components;
|
|
lua_pop(L, components);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (const Buffer::DataMember &member : vertexformat)
|
|
{
|
|
// Fetch the values from Lua and store them in data buffer.
|
|
luax_writebufferdata(L, idx, member.decl.format, data + member.offset);
|
|
idx += member.info.components;
|
|
}
|
|
}
|
|
|
|
t->setVertexDataModified(offset, t->getVertexStride());
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getVertex(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
|
|
|
|
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
|
|
|
const char *data = nullptr;
|
|
luax_catchexcept(L, [&](){ data = (const char *) t->checkVertexDataOffset(index, nullptr); });
|
|
|
|
int n = 0;
|
|
|
|
for (const Buffer::DataMember &member : vertexformat)
|
|
{
|
|
luax_readbufferdata(L, member.decl.format, data + member.offset);
|
|
n += member.info.components;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
int w_Mesh_setVertexAttribute(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
|
|
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
|
|
|
|
const auto &vertexformat = t->getVertexFormat();
|
|
|
|
if (attribindex < 0 || attribindex >= (int) vertexformat.size())
|
|
return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
|
|
|
|
const Buffer::DataMember &member = vertexformat[attribindex];
|
|
|
|
char *data = nullptr;
|
|
size_t offset = 0;
|
|
luax_catchexcept(L, [&](){ data = (char *) t->checkVertexDataOffset(vertindex, &offset); });
|
|
|
|
// Fetch the values from Lua and store them in the data buffer.
|
|
luax_writebufferdata(L, 4, member.decl.format, data + member.offset);
|
|
|
|
t->setVertexDataModified(offset + member.offset, member.size);
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getVertexAttribute(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
|
|
int attribindex = (int) luaL_checkinteger(L, 3) - 1;
|
|
|
|
const auto &vertexformat = t->getVertexFormat();
|
|
|
|
if (attribindex < 0 || attribindex >= (int) vertexformat.size())
|
|
return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
|
|
|
|
const Buffer::DataMember &member = vertexformat[attribindex];
|
|
|
|
const char *data = nullptr;
|
|
luax_catchexcept(L, [&](){ data = (const char *) t->checkVertexDataOffset(vertindex, nullptr); });
|
|
|
|
luax_readbufferdata(L, member.decl.format, data + member.offset);
|
|
return member.info.components;
|
|
}
|
|
|
|
int w_Mesh_getVertexCount(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
lua_pushinteger(L, t->getVertexCount());
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_getVertexFormat(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
|
|
lua_createtable(L, (int) vertexformat.size(), 0);
|
|
|
|
const char *tname = nullptr;
|
|
|
|
for (size_t i = 0; i < vertexformat.size(); i++)
|
|
{
|
|
const auto &decl = vertexformat[i].decl;
|
|
|
|
if (!getConstant(decl.format, tname))
|
|
return luax_enumerror(L, "vertex attribute data type", getConstants(decl.format), tname);
|
|
|
|
lua_createtable(L, 3, 0);
|
|
|
|
lua_pushstring(L, decl.name.c_str());
|
|
lua_rawseti(L, -2, 1);
|
|
|
|
lua_pushstring(L, tname);
|
|
lua_rawseti(L, -2, 2);
|
|
|
|
// format[i] = {name, type}
|
|
lua_rawseti(L, -2, (int) i + 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_setAttributeEnabled(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
const char *name = luaL_checkstring(L, 2);
|
|
bool enable = luax_checkboolean(L, 3);
|
|
luax_catchexcept(L, [&](){ t->setAttributeEnabled(name, enable); });
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_isAttributeEnabled(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
const char *name = luaL_checkstring(L, 2);
|
|
bool enabled = false;
|
|
luax_catchexcept(L, [&](){ enabled = t->isAttributeEnabled(name); });
|
|
lua_pushboolean(L, enabled);
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_attachAttribute(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
const char *name = luaL_checkstring(L, 2);
|
|
|
|
Buffer *buffer = nullptr;
|
|
Mesh *mesh = nullptr;
|
|
if (luax_istype(L, 3, Buffer::type))
|
|
{
|
|
buffer = luax_checktype<Buffer>(L, 3);
|
|
}
|
|
else
|
|
{
|
|
mesh = luax_checkmesh(L, 3);
|
|
buffer = mesh->getVertexBuffer();
|
|
if (buffer == nullptr)
|
|
return luaL_error(L, "Mesh does not have its own vertex buffer.");
|
|
}
|
|
|
|
AttributeStep step = STEP_PER_VERTEX;
|
|
const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4);
|
|
if (stepstr != nullptr && !getConstant(stepstr, step))
|
|
return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr);
|
|
|
|
const char *attachname = luaL_optstring(L, 5, name);
|
|
int startindex = (int) luaL_optinteger(L, 6, 1) - 1;
|
|
|
|
luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, mesh, attachname, startindex, step); });
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_detachAttribute(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
const char *name = luaL_checkstring(L, 2);
|
|
bool success = false;
|
|
luax_catchexcept(L, [&](){ success = t->detachAttribute(name); });
|
|
luax_pushboolean(L, success);
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_getAttachedAttributes(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
const auto &attributes = t->getAttachedAttributes();
|
|
|
|
lua_createtable(L, (int) attributes.size(), 0);
|
|
|
|
for (int i = 0; i < (int) attributes.size(); i++)
|
|
{
|
|
const auto &attrib = attributes[i];
|
|
|
|
lua_createtable(L, 5, 0);
|
|
|
|
luax_pushstring(L, attrib.name);
|
|
lua_rawseti(L, -1, 1);
|
|
|
|
luax_pushtype(L, attrib.buffer.get());
|
|
lua_rawseti(L, -1, 2);
|
|
|
|
const char *stepstr = nullptr;
|
|
if (!getConstant(attrib.step, stepstr))
|
|
return luaL_error(L, "Invalid vertex attribute step.");
|
|
lua_pushstring(L, stepstr);
|
|
lua_rawseti(L, -1, 3);
|
|
|
|
const Buffer::DataMember &member = attrib.buffer->getDataMember(attrib.indexInBuffer);
|
|
luax_pushstring(L, member.decl.name);
|
|
lua_rawseti(L, -1, 4);
|
|
|
|
lua_pushinteger(L, attrib.startArrayIndex + 1);
|
|
lua_rawseti(L, -1, 5);
|
|
|
|
lua_rawseti(L, -1, i + 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_getVertexBuffer(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
luax_pushtype(L, t->getVertexBuffer());
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_flush(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
t->flush();
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_setVertexMap(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
if (lua_isnoneornil(L, 2))
|
|
{
|
|
// Disable the vertex map / index buffer.
|
|
luax_catchexcept(L, [&](){ t->setVertexMap(); });
|
|
return 0;
|
|
}
|
|
|
|
if (luax_istype(L, 2, Data::type))
|
|
{
|
|
Data *d = luax_totype<Data>(L, 2, Data::type);
|
|
|
|
const char *indextypestr = luaL_checkstring(L, 3);
|
|
IndexDataType indextype;
|
|
if (!getConstant(indextypestr, indextype))
|
|
return luax_enumerror(L, "index data type", getConstants(indextype), indextypestr);
|
|
|
|
size_t datatypesize = getIndexDataSize(indextype);
|
|
|
|
int indexcount = (int) luaL_optinteger(L, 4, d->getSize() / datatypesize);
|
|
|
|
if (indexcount < 1 || indexcount * datatypesize > d->getSize())
|
|
return luaL_error(L, "Invalid index count: %d", indexcount);
|
|
|
|
luax_catchexcept(L, [&]() { t->setVertexMap(indextype, d->getData(), indexcount * datatypesize); });
|
|
return 0;
|
|
}
|
|
|
|
bool is_table = lua_istable(L, 2);
|
|
int nargs = is_table ? (int) luax_objlen(L, 2) : lua_gettop(L) - 1;
|
|
|
|
std::vector<uint32> vertexmap;
|
|
vertexmap.reserve(nargs);
|
|
|
|
if (is_table)
|
|
{
|
|
for (int i = 0; i < nargs; i++)
|
|
{
|
|
lua_rawgeti(L, 2, i + 1);
|
|
vertexmap.push_back(uint32(luaL_checkinteger(L, -1) - 1));
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < nargs; i++)
|
|
vertexmap.push_back(uint32(luaL_checkinteger(L, i + 2) - 1));
|
|
}
|
|
|
|
luax_catchexcept(L, [&](){ t->setVertexMap(vertexmap); });
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getVertexMap(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
std::vector<uint32> vertex_map;
|
|
bool has_vertex_map = false;
|
|
luax_catchexcept(L, [&](){ has_vertex_map = t->getVertexMap(vertex_map); });
|
|
|
|
if (!has_vertex_map)
|
|
{
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
int element_count = (int) vertex_map.size();
|
|
|
|
lua_createtable(L, element_count, 0);
|
|
|
|
for (int i = 0; i < element_count; i++)
|
|
{
|
|
lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
|
|
lua_rawseti(L, -2, i + 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_setIndexBuffer(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
Buffer *b = nullptr;
|
|
if (!lua_isnoneornil(L, 2))
|
|
b = luax_checkbuffer(L, 2);
|
|
luax_catchexcept(L, [&]() { t->setIndexBuffer(b); });
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getIndexBuffer(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
luax_pushtype(L, t->getIndexBuffer());
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_setTexture(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
if (lua_isnoneornil(L, 2))
|
|
t->setTexture();
|
|
else
|
|
{
|
|
Texture *tex = luax_checktexture(L, 2);
|
|
luax_catchexcept(L, [&](){ t->setTexture(tex); });
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getTexture(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
Texture *tex = t->getTexture();
|
|
|
|
if (tex == nullptr)
|
|
return 0;
|
|
|
|
luax_pushtype(L, tex);
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_setDrawMode(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
const char *str = luaL_checkstring(L, 2);
|
|
PrimitiveType mode;
|
|
|
|
if (!getConstant(str, mode))
|
|
return luax_enumerror(L, "mesh draw mode", getConstants(mode), str);
|
|
|
|
t->setDrawMode(mode);
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getDrawMode(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
PrimitiveType mode = t->getDrawMode();
|
|
const char *str;
|
|
|
|
if (!getConstant(mode, str))
|
|
return luaL_error(L, "Unknown mesh draw mode.");
|
|
|
|
lua_pushstring(L, str);
|
|
return 1;
|
|
}
|
|
|
|
int w_Mesh_setDrawRange(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
if (lua_isnoneornil(L, 2))
|
|
t->setDrawRange();
|
|
else
|
|
{
|
|
int start = (int) luaL_checkinteger(L, 2) - 1;
|
|
int count = (int) luaL_checkinteger(L, 3);
|
|
luax_catchexcept(L, [&](){ t->setDrawRange(start, count); });
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int w_Mesh_getDrawRange(lua_State *L)
|
|
{
|
|
Mesh *t = luax_checkmesh(L, 1);
|
|
|
|
int start = 0;
|
|
int count = 1;
|
|
if (!t->getDrawRange(start, count))
|
|
return 0;
|
|
|
|
lua_pushinteger(L, start + 1);
|
|
lua_pushinteger(L, count);
|
|
return 2;
|
|
}
|
|
|
|
static const luaL_Reg w_Mesh_functions[] =
|
|
{
|
|
{ "setVertices", w_Mesh_setVertices },
|
|
{ "setVertex", w_Mesh_setVertex },
|
|
{ "getVertex", w_Mesh_getVertex },
|
|
{ "setVertexAttribute", w_Mesh_setVertexAttribute },
|
|
{ "getVertexAttribute", w_Mesh_getVertexAttribute },
|
|
{ "getVertexCount", w_Mesh_getVertexCount },
|
|
{ "getVertexFormat", w_Mesh_getVertexFormat },
|
|
{ "setAttributeEnabled", w_Mesh_setAttributeEnabled },
|
|
{ "isAttributeEnabled", w_Mesh_isAttributeEnabled },
|
|
{ "attachAttribute", w_Mesh_attachAttribute },
|
|
{ "detachAttribute", w_Mesh_detachAttribute },
|
|
{ "getAttachedAttributes", w_Mesh_getAttachedAttributes },
|
|
{ "getVertexBuffer", w_Mesh_getVertexBuffer },
|
|
{ "flush", w_Mesh_flush },
|
|
{ "setVertexMap", w_Mesh_setVertexMap },
|
|
{ "getVertexMap", w_Mesh_getVertexMap },
|
|
{ "setIndexBuffer", w_Mesh_setIndexBuffer },
|
|
{ "getIndexBuffer", w_Mesh_getIndexBuffer },
|
|
{ "setTexture", w_Mesh_setTexture },
|
|
{ "getTexture", w_Mesh_getTexture },
|
|
{ "setDrawMode", w_Mesh_setDrawMode },
|
|
{ "getDrawMode", w_Mesh_getDrawMode },
|
|
{ "setDrawRange", w_Mesh_setDrawRange },
|
|
{ "getDrawRange", w_Mesh_getDrawRange },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
extern "C" int luaopen_mesh(lua_State *L)
|
|
{
|
|
return luax_register_type(L, &Mesh::type, w_Mesh_functions, nullptr);
|
|
}
|
|
|
|
} // graphics
|
|
} // love
|