|
| 1 | +// SPDX-FileCopyrightText: 2026 Giovanni MARIANO |
| 2 | +// |
| 3 | +// SPDX-License-Identifier: MPL-2.0 |
| 4 | + |
| 5 | +#include "alea_lua.h" |
| 6 | +#include "alea_mesh.h" |
| 7 | +#include <stdlib.h> |
| 8 | + |
| 9 | +/* ============================================================================ |
| 10 | + * Userdata |
| 11 | + * ============================================================================ */ |
| 12 | + |
| 13 | +typedef struct { |
| 14 | + alea_mesh_result_t* ptr; |
| 15 | +} alea_lua_mesh_result_t; |
| 16 | + |
| 17 | +static alea_lua_mesh_result_t* check_meshresult(lua_State* L, int idx) { |
| 18 | + return (alea_lua_mesh_result_t*)luaL_checkudata(L, idx, ALEA_MESHRESULT_MT); |
| 19 | +} |
| 20 | + |
| 21 | +/* ============================================================================ |
| 22 | + * Config helper: Lua table -> alea_mesh_config_t |
| 23 | + * ============================================================================ */ |
| 24 | + |
| 25 | +static void lua_to_mesh_config(lua_State* L, int idx, alea_mesh_config_t* cfg) { |
| 26 | + luaL_checktype(L, idx, LUA_TTABLE); |
| 27 | + |
| 28 | + lua_getfield(L, idx, "x_min"); |
| 29 | + if (!lua_isnil(L, -1)) cfg->x_min = lua_tonumber(L, -1); |
| 30 | + lua_pop(L, 1); |
| 31 | + lua_getfield(L, idx, "x_max"); |
| 32 | + if (!lua_isnil(L, -1)) cfg->x_max = lua_tonumber(L, -1); |
| 33 | + lua_pop(L, 1); |
| 34 | + lua_getfield(L, idx, "y_min"); |
| 35 | + if (!lua_isnil(L, -1)) cfg->y_min = lua_tonumber(L, -1); |
| 36 | + lua_pop(L, 1); |
| 37 | + lua_getfield(L, idx, "y_max"); |
| 38 | + if (!lua_isnil(L, -1)) cfg->y_max = lua_tonumber(L, -1); |
| 39 | + lua_pop(L, 1); |
| 40 | + lua_getfield(L, idx, "z_min"); |
| 41 | + if (!lua_isnil(L, -1)) cfg->z_min = lua_tonumber(L, -1); |
| 42 | + lua_pop(L, 1); |
| 43 | + lua_getfield(L, idx, "z_max"); |
| 44 | + if (!lua_isnil(L, -1)) cfg->z_max = lua_tonumber(L, -1); |
| 45 | + lua_pop(L, 1); |
| 46 | + |
| 47 | + lua_getfield(L, idx, "nx"); |
| 48 | + if (!lua_isnil(L, -1)) cfg->nx = (int)lua_tointeger(L, -1); |
| 49 | + lua_pop(L, 1); |
| 50 | + lua_getfield(L, idx, "ny"); |
| 51 | + if (!lua_isnil(L, -1)) cfg->ny = (int)lua_tointeger(L, -1); |
| 52 | + lua_pop(L, 1); |
| 53 | + lua_getfield(L, idx, "nz"); |
| 54 | + if (!lua_isnil(L, -1)) cfg->nz = (int)lua_tointeger(L, -1); |
| 55 | + lua_pop(L, 1); |
| 56 | + |
| 57 | + lua_getfield(L, idx, "format"); |
| 58 | + if (!lua_isnil(L, -1)) cfg->format = (alea_mesh_format_t)(int)lua_tointeger(L, -1); |
| 59 | + lua_pop(L, 1); |
| 60 | + |
| 61 | + lua_getfield(L, idx, "void_material_id"); |
| 62 | + if (!lua_isnil(L, -1)) cfg->void_material_id = (int)lua_tointeger(L, -1); |
| 63 | + lua_pop(L, 1); |
| 64 | + |
| 65 | + lua_getfield(L, idx, "auto_pad"); |
| 66 | + if (!lua_isnil(L, -1)) cfg->auto_pad = lua_tonumber(L, -1); |
| 67 | + lua_pop(L, 1); |
| 68 | +} |
| 69 | + |
| 70 | +/* ============================================================================ |
| 71 | + * System methods |
| 72 | + * ============================================================================ */ |
| 73 | + |
| 74 | +/* sys:mesh_sample(config) -> MeshResult */ |
| 75 | +static int l_mesh_sample(lua_State* L) { |
| 76 | + alea_system_t* sys = alea_get_sys(L, 1); |
| 77 | + alea_mesh_config_t cfg; |
| 78 | + alea_mesh_config_init(&cfg); |
| 79 | + if (lua_istable(L, 2)) |
| 80 | + lua_to_mesh_config(L, 2, &cfg); |
| 81 | + |
| 82 | + alea_lua_mesh_result_t* ud = (alea_lua_mesh_result_t*)lua_newuserdata( |
| 83 | + L, sizeof(alea_lua_mesh_result_t)); |
| 84 | + ud->ptr = NULL; |
| 85 | + luaL_setmetatable(L, ALEA_MESHRESULT_MT); |
| 86 | + |
| 87 | + ud->ptr = alea_mesh_sample(sys, &cfg); |
| 88 | + if (!ud->ptr) |
| 89 | + return luaL_error(L, "mesh_sample failed: %s", alea_error()); |
| 90 | + return 1; |
| 91 | +} |
| 92 | + |
| 93 | +/* sys:mesh_export(config, filename) */ |
| 94 | +static int l_mesh_export_system(lua_State* L) { |
| 95 | + alea_system_t* sys = alea_get_sys(L, 1); |
| 96 | + alea_mesh_config_t cfg; |
| 97 | + alea_mesh_config_init(&cfg); |
| 98 | + if (lua_istable(L, 2)) |
| 99 | + lua_to_mesh_config(L, 2, &cfg); |
| 100 | + const char* filename = luaL_checkstring(L, 3); |
| 101 | + |
| 102 | + if (alea_mesh_export_system(sys, &cfg, filename) != 0) |
| 103 | + return luaL_error(L, "mesh_export failed: %s", alea_error()); |
| 104 | + return 0; |
| 105 | +} |
| 106 | + |
| 107 | +/* ============================================================================ |
| 108 | + * MeshResult methods |
| 109 | + * ============================================================================ */ |
| 110 | + |
| 111 | +/* mesh:export(format, filename) */ |
| 112 | +static int l_meshresult_export(lua_State* L) { |
| 113 | + alea_lua_mesh_result_t* ud = check_meshresult(L, 1); |
| 114 | + if (!ud->ptr) return luaL_error(L, "mesh result freed"); |
| 115 | + int fmt = (int)luaL_checkinteger(L, 2); |
| 116 | + const char* filename = luaL_checkstring(L, 3); |
| 117 | + if (alea_mesh_export(ud->ptr, (alea_mesh_format_t)fmt, filename) != 0) |
| 118 | + return luaL_error(L, "mesh:export failed: %s", alea_error()); |
| 119 | + return 0; |
| 120 | +} |
| 121 | + |
| 122 | +/* mesh:info() -> table */ |
| 123 | +static int l_meshresult_info(lua_State* L) { |
| 124 | + alea_lua_mesh_result_t* ud = check_meshresult(L, 1); |
| 125 | + if (!ud->ptr) return luaL_error(L, "mesh result freed"); |
| 126 | + alea_mesh_result_t* m = ud->ptr; |
| 127 | + |
| 128 | + lua_createtable(L, 0, 5); |
| 129 | + lua_pushinteger(L, m->nx); lua_setfield(L, -2, "nx"); |
| 130 | + lua_pushinteger(L, m->ny); lua_setfield(L, -2, "ny"); |
| 131 | + lua_pushinteger(L, m->nz); lua_setfield(L, -2, "nz"); |
| 132 | + lua_pushinteger(L, m->num_materials); lua_setfield(L, -2, "num_materials"); |
| 133 | + |
| 134 | + /* unique_materials */ |
| 135 | + lua_createtable(L, m->num_materials, 0); |
| 136 | + for (int i = 0; i < m->num_materials; i++) { |
| 137 | + lua_pushinteger(L, m->unique_materials[i]); |
| 138 | + lua_rawseti(L, -2, i + 1); |
| 139 | + } |
| 140 | + lua_setfield(L, -2, "unique_materials"); |
| 141 | + return 1; |
| 142 | +} |
| 143 | + |
| 144 | +/* mesh:material_ids() -> flat table */ |
| 145 | +static int l_meshresult_material_ids(lua_State* L) { |
| 146 | + alea_lua_mesh_result_t* ud = check_meshresult(L, 1); |
| 147 | + if (!ud->ptr) return luaL_error(L, "mesh result freed"); |
| 148 | + alea_mesh_result_t* m = ud->ptr; |
| 149 | + size_t total = (size_t)m->nx * (size_t)m->ny * (size_t)m->nz; |
| 150 | + lua_createtable(L, (int)total, 0); |
| 151 | + for (size_t i = 0; i < total; i++) { |
| 152 | + lua_pushinteger(L, m->material_ids[i]); |
| 153 | + lua_rawseti(L, -2, (lua_Integer)(i + 1)); |
| 154 | + } |
| 155 | + return 1; |
| 156 | +} |
| 157 | + |
| 158 | +/* mesh:cell_ids() -> flat table */ |
| 159 | +static int l_meshresult_cell_ids(lua_State* L) { |
| 160 | + alea_lua_mesh_result_t* ud = check_meshresult(L, 1); |
| 161 | + if (!ud->ptr) return luaL_error(L, "mesh result freed"); |
| 162 | + alea_mesh_result_t* m = ud->ptr; |
| 163 | + size_t total = (size_t)m->nx * (size_t)m->ny * (size_t)m->nz; |
| 164 | + lua_createtable(L, (int)total, 0); |
| 165 | + for (size_t i = 0; i < total; i++) { |
| 166 | + lua_pushinteger(L, m->cell_ids[i]); |
| 167 | + lua_rawseti(L, -2, (lua_Integer)(i + 1)); |
| 168 | + } |
| 169 | + return 1; |
| 170 | +} |
| 171 | + |
| 172 | +/* __gc */ |
| 173 | +static int l_meshresult_gc(lua_State* L) { |
| 174 | + alea_lua_mesh_result_t* ud = check_meshresult(L, 1); |
| 175 | + if (ud->ptr) { |
| 176 | + alea_mesh_result_free(ud->ptr); |
| 177 | + ud->ptr = NULL; |
| 178 | + } |
| 179 | + return 0; |
| 180 | +} |
| 181 | + |
| 182 | +/* ============================================================================ |
| 183 | + * Registration |
| 184 | + * ============================================================================ */ |
| 185 | + |
| 186 | +static const luaL_Reg mesh_system_methods[] = { |
| 187 | + {"mesh_sample", l_mesh_sample}, |
| 188 | + {"mesh_export", l_mesh_export_system}, |
| 189 | + {NULL, NULL} |
| 190 | +}; |
| 191 | + |
| 192 | +static const luaL_Reg meshresult_meta[] = { |
| 193 | + {"__gc", l_meshresult_gc}, |
| 194 | + {NULL, NULL} |
| 195 | +}; |
| 196 | + |
| 197 | +static const luaL_Reg meshresult_methods[] = { |
| 198 | + {"export", l_meshresult_export}, |
| 199 | + {"info", l_meshresult_info}, |
| 200 | + {"material_ids", l_meshresult_material_ids}, |
| 201 | + {"cell_ids", l_meshresult_cell_ids}, |
| 202 | + {NULL, NULL} |
| 203 | +}; |
| 204 | + |
| 205 | +int luaopen_alea_mesh(lua_State* L) { |
| 206 | + /* Add system methods */ |
| 207 | + luaL_getmetatable(L, ALEA_SYSTEM_MT); |
| 208 | + lua_getfield(L, -1, "__index"); |
| 209 | + luaL_setfuncs(L, mesh_system_methods, 0); |
| 210 | + lua_pop(L, 2); |
| 211 | + |
| 212 | + /* Create MeshResult metatable */ |
| 213 | + luaL_newmetatable(L, ALEA_MESHRESULT_MT); |
| 214 | + luaL_setfuncs(L, meshresult_meta, 0); |
| 215 | + lua_newtable(L); |
| 216 | + luaL_setfuncs(L, meshresult_methods, 0); |
| 217 | + lua_setfield(L, -2, "__index"); |
| 218 | + lua_pop(L, 1); |
| 219 | + |
| 220 | + return 0; |
| 221 | +} |
0 commit comments